[
  {
    "path": ".github/FUNDING.yml",
    "content": "# multiple github accounts are allowed, but only one of everything else. Setup is explained in:\n# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository\ngithub: [spacecowboy, CampelloManuel]\nko_fi: spacecowboy\ncustom: https://www.paypal.com/paypalme/campellomanuel\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Report issues of the app's features\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n### Describe the bug\n\nwrite a clear and short description of what the bug is.\n\n### how To Reproduce\n\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\nalso state what is the expected result and what is the actual result.\n\n### If applicable\n\n* add the stacktrace\n* add logcat output as explained [here](https://f-droid.org/docs/Getting_logcat_messages_after_crash/)\n\n### Technical information\n\n- Device: [e.g. Nexus 7 (2013)]\n- OS: [e.g. LineageOS 15.1]\n- app version: [e.g. v3.2.4]\n- app commit id (only for nightly builds): [e.g. 4b333bb]\n\nIf relevant, please say what your sync settings are, and report any odd stuff happening in general.\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for the app\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/notifications_do_not_appear.md",
    "content": "---\nname: Notifications don't show up\nabout: The app does not show notifications for reminders\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Please describe the problem and how to reproduce it:**\n\n...\n\n**Checklist**\n\n- [ ] Consulted [https://dontkillmyapp.com/]\n- [ ] Power management: battery optimization is disabled for OpenTracks\n\n**Technical information**\n\n- Device: [e.g. Nexus 7 (2013)]\n- OS: [e.g. LineageOS 15.1]\n- app version: [e.g. v3.2.4]\n- app commit id (only for nightly builds): [e.g. 4b333bb]"
  },
  {
    "path": ".github/workflows/android_build.yml",
    "content": "name: Android build\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n\njobs:\n  # this job builds and uploads the apk\n  build_the_apk:\n    # there's no need to run it on forks\n    if: github.repository == 'spacecowboy/NotePad'\n    runs-on: ubuntu-latest    \n    permissions:\n      contents: read    \n    steps:\n    - name: perform the checkout\n      uses: actions/checkout@v4\n      \n    - name: perform the validation\n      uses: gradle/actions/wrapper-validation@v3\n      \n    - name: perform the JDK setup\n      uses: actions/setup-java@v4\n      with:\n        java-version: '21'\n        distribution: 'temurin'\n        cache: 'gradle'\n\n    - name: perform Gradle caching\n      uses: gradle/actions/setup-gradle@v3\n\n    - name: perform the Gradle build\n      run: ./gradlew build\n\n    - name: perform additional checks with our custom tasks\n      run: ./gradlew checkLanguages checkFastlane\n\n    # TODO output files from this step are not saved. Take a look at them\n    - name: perform lint checks with gradle\n      run: ./gradlew lint\n\n    - name: perform the APK upload\n      uses: actions/upload-artifact@v4\n      with:\n        name: app_debug\n        path: app/build/outputs/apk/debug/app-debug.apk\n        retention-days: 7 # we're not publishing the app: nobody needs this apk\n"
  },
  {
    "path": ".github/workflows/android_tests.yml",
    "content": "name: Android tests\n\non:\n  push:\n    branches: [ \"master\" ]\n    # runs when you commit to the master branch and when merging pull requests.\n    # Not on receiving pull requests, as it would be a waste.\n    paths-ignore:\n      - '**.md'\n      - '.github/**'\n      - '!.github/workflows/build.yml'\njobs:\n  # this job runs all the tests\n  job_tests:\n    if: github.repository == 'spacecowboy/NotePad' # no need to run it on forks    \n    timeout-minutes: 45 # test jobs take ~11 minutes each. We give time to download the AVD images\n    continue-on-error: true # run tests for ALL configurations, don't stop at the 1° failure\n    strategy:\n      matrix:\n        # test on emulators with these Android API versions. They all have ATD images.\n        api-level: [ 30, 32, 34, 35 ]\n        # use stripped-down, test-friendly Android OS images without, or with, google apps\n        target: [ aosp_atd, google_atd ]\n        # profile: [ 5.1in WVGA, 10.1in WXGA ] # tests on tablets don't work anyway...\n    runs-on: ubuntu-latest # List of installed software:\n    # https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md\n    permissions:\n      contents: read\n    steps:\n      - name: perform the checkout\n        uses: actions/checkout@v4\n\n      - name: perform the set up for the JDK\n        uses: actions/setup-java@v4\n        with:\n          java-version: 21\n          distribution: temurin\n\n      - name: perform the validation\n        continue-on-error: true\n        uses: gradle/actions/wrapper-validation@v3\n\n      - name: perform Gradle setup & caching\n        uses: gradle/actions/setup-gradle@v3\n\n      - name: Check for hardware acceleration availability\n        continue-on-error: true\n        # Read the console logs for the result. The android emulator can't use\n        # acceleration, according to the logs. No you can't install HAXM.\n        run: $ANDROID_HOME/emulator/emulator -accel-check\n\n      - name: Enable KVM group perms as required by reactivecircus/android-emulator-runner\n        run: |\n          echo 'KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0666\", OPTIONS+=\"static_node=kvm\"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules\n          sudo udevadm control --reload-rules\n          sudo udevadm trigger --name-match=kvm  \n\n      - name: perform AVD caching\n        uses: actions/cache@v4\n        id: avd-cache\n        # they appear in\n        # https://github.com/spacecowboy/NotePad/actions/caches?query=sort%3Asize-desc\n        with:\n          path: |\n            ~/.android/avd/*\n            ~/.android/adb*\n          key: avd-${{ matrix.api-level }}-${{ matrix.target }}\n\n      - name: create AVD and generate snapshot for caching\n        # here it automatically installs the needed android SDK components\n        # see https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md\n        if: steps.avd-cache.outputs.cache-hit != 'true'\n        uses: reactivecircus/android-emulator-runner@v2\n        with:\n          api-level: ${{ matrix.api-level }}\n          target: ${{ matrix.target }}\n          profile: 5.1in WVGA # low resolution: faster. This has no software buttons\n          arch: x86_64\n          disk-size: 2G       # needed for saving org and json files. We need 2 GB for API 34\n          force-avd-creation: false\n          # The emulator is picky on these parameters. For...\n          # tests with \"-accel on\": see github run #84\n          # tests with \"-gpu host\": the emulators never boot!\n          # tests with \"-gpu off\": see github run #87, faster & more stable\n          emulator-options: -no-boot-anim -no-window -gpu off\n          disable-animations: true\n          disable-spellchecker: true\n          # starts the emulator, runs this script, then closes the emulator\n          script: echo \"Generated AVD snapshot for caching.\"\n\n      - name: perform the Gradle build\n        run: ./gradlew build\n\n      - name: run custom tasks for additional checks\n        run: ./gradlew checkLanguages checkFastlane\n\n      - name: make the emulator script executable\n        run: chmod +x github_on_emu_started.sh\n\n      - name: run the tests\n        # note that by now the app is already built\n        uses: reactivecircus/android-emulator-runner@v2\n        with:\n          api-level: ${{ matrix.api-level }}\n          target: ${{ matrix.target }}\n          arch: x86_64\n          profile: 5.4in FWVGA\n          disk-size: 500M\n          force-avd-creation: false\n          disable-animations: true\n          disable-spellchecker: true\n          emulator-options: -no-snapshot-save -verbose -no-boot-anim -no-window -gpu off\n          script: bash ./github_on_emu_started.sh\n\n      - name: upload the generated files\n        uses: actions/upload-artifact@v4\n        if: always()\n        with:\n          name: files-api${{ matrix.api-level }}-${{ matrix.target }}\n          path: |\n            logcat-dump.txt\n            app/build/reports/androidTests/**\n"
  },
  {
    "path": ".github/workflows/publish_playstore.yml",
    "content": "name: Publish to Play store\n\non:\n  push:\n    # Branch only during testing\n    #branches:\n    #  - master\n    tags:\n      - '*'\n\njobs:\n  build_and_deploy:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    steps:\n    - name: perform the checkout\n      uses: actions/checkout@v4\n      with:\n        submodules: 'recursive'\n        fetch-depth: 0\n\n    - name: perform the validation\n      uses: gradle/actions/wrapper-validation@v3\n\n    - name: perform the JDK setup\n      uses: actions/setup-java@v4\n      with:\n        java-version: '21'\n        distribution: 'temurin'\n        cache: 'gradle'\n\n    - name: perform Gradle caching\n      uses: gradle/actions/setup-gradle@v3\n\n    - name: run custom tasks to ensure app can be published on play store\n      run: ./gradlew checkLanguages checkFastlane\n\n    - name: build and deploy\n      run: ./deploy_playstore.sh\n      env:\n        SERVICEACCOUNTJSON: ${{ secrets.SERVICEACCOUNTJSON }}\n        KEYSTOREPASSWORD: ${{ secrets.KEYSTOREPASSWORD }}\n        KEYSTORE: ${{ secrets.KEYSTORE }}\n        KEYPASSWORD: ${{ secrets.KEYPASSWORD }}\n        KEYALIAS: ${{ secrets.KEYALIAS }}\n"
  },
  {
    "path": ".gitignore",
    "content": "secret.properties\n_release_keys_\nNotePad_ant\nNotePad_ant/*\n.factorypath\nbin/\nbuild/\n.gradle\ngen/\nNotePad.apk\nNotes_for_ICS_Market_Link.png\nicon/\nproguard/\n.apt_generated\n.settings*\ntranslate-temp/\n*~\n*iml\n.idea\nlocal.properties\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n\n"
  },
  {
    "path": "README.md",
    "content": "# NoNonsense Notes\n\n[![Android build](https://github.com/spacecowboy/NotePad/actions/workflows/android_build.yml/badge.svg)](https://github.com/spacecowboy/NotePad/actions/workflows/android_build.yml)          [![Android tests](https://github.com/spacecowboy/NotePad/actions/workflows/android_tests.yml/badge.svg)](https://github.com/spacecowboy/NotePad/actions/workflows/android_tests.yml)     [![Translation status](https://hosted.weblate.org/widgets/no-nonsense-notes/-/android-strings/svg-badge.svg)](https://hosted.weblate.org/engage/no-nonsense-notes/) \\\n<img src=\"https://img.shields.io/f-droid/v/com.nononsenseapps.notepad.svg?logo=F-Droid\"/> <img src=\"https://img.shields.io/github/release/spacecowboy/NotePad.svg?logo=github\"/> <img src=\"https://img.shields.io/github/release-date/spacecowboy/NotePad\"/> \\\n<img src=\"https://img.shields.io/github/last-commit/spacecowboy/NotePad\"/> <img src=\"https://img.shields.io/github/search/spacecowboy/NotePad/TODO\"/> <img src=\"https://img.shields.io/librariesio/github/spacecowboy/NotePad\"/>\n\n_A note-taking app for Android with reminders, since 2012_\n\n[<img src=\"https://fdroid.gitlab.io/artwork/badge/get-it-on.png\" alt=\"Get it on F-Droid\" height=\"80\">](https://f-droid.org/repository/browse/?fdid=com.nononsenseapps.notepad)     [<img alt=\"Get it on Google Play\" src=\"https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png\" height=\"80\">](https://play.google.com/store/apps/details?id=com.nononsenseapps.notepad.play)\n\n<img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/1.png\" alt=\"Phone UI\" height=\"480\" /> _ <img src=\"fastlane/metadata/android/en-US/images/tenInchScreenshots/1.png\" alt=\"Tablet UI\" height=\"360\" />\n\n## Translation\n\nHelp translate the app on [Hosted Weblate](https://hosted.weblate.org/projects/no-nonsense-notes/) \\\n<a href=\"https://hosted.weblate.org/engage/no-nonsense-notes/\">\n<img src=\"https://hosted.weblate.org/widgets/no-nonsense-notes/-/horizontal-auto.svg\" alt=\"Translation status\" />\n</a>\n\n## How does it work ?\n\nRead the [tutorial](./documents/TUTORIAL.md) to learn about the app,\nor the [FAQ](./documents/FAQ.md) for other kinds of questions.\n\n## Reporting bugs\n\nPlease [report bugs](https://github.com/spacecowboy/NotePad/issues) using the provided template.\n\n## Build the project\n\n```sh\ngit clone https://github.com/spacecowboy/NotePad\ncd NotePad\n./gradlew check\n./gradlew installDebug\n```\n\nif it does not work, [open an issue](https://github.com/spacecowboy/NotePad/issues)\n\n## GPLv3+ License\n\n<details>\n\n```text\nCopyright (C) 2014 Jonas Kalderstam\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n```\n\n</details>\n\n[Full license](./LICENSE)\n\n## Useful links\n\n* [FAQ](./documents/FAQ.md)\n* [app tutorial](./documents/TUTORIAL.md)\n* [Contribution guide](./documents/CONTRIBUTING.md)\n* [Releases](https://github.com/spacecowboy/NotePad/releases)\n* [Privacy policy](./documents/PRIVACY.md)\n* [f-droid forum](https://forum.f-droid.org/t/i-want-to-update-an-old-discontinued-app-nononsense-notes/20037)\n\n\nBuilt by @spacecowboy, maintained by @CampelloManuel. \\\nThe app is currently being updated. Old versions are still available on F-droid.\n"
  },
  {
    "path": "app/build.gradle",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\napply plugin: 'com.android.application'\n// apply plugin: 'kotlin-android' we don't use kotlin for this app\n\nandroid {\n    compileSdk = 36\n    namespace = \"com.nononsenseapps.notepad\" // for R.*\n\n    buildFeatures {\n        buildConfig = true\n        // replaces part of the androidannotations library. See mBinding in java classes\n        viewBinding = true\n        // allow declaring different string resources for different buildTypes\n        resValues = true\n        // these features are useless for us\n        compose = false\n        aidl = false\n        renderScript = false\n        shaders = false\n    }\n\n    lint {\n        htmlReport = false // TODO re-enable and fix all warnings reported in the html file\n        textReport = false\n        abortOnError = false\n        explainIssues = true\n        checkTestSources = true\n        // turn off checking the given issue id's\n        disable += ['RtlHardcoded', 'ApplySharedPref']\n    }\n\n    packagingOptions {\n        resources {\n            // excludes += ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt']\n        }\n    }\n\n    if (project.hasProperty('STORE_FILE')) {\n        signingConfigs {\n            release {\n                storeFile file(STORE_FILE)\n                storePassword STORE_PASSWORD\n                keyAlias KEY_ALIAS\n                keyPassword KEY_PASSWORD\n            }\n        }\n    } else {\n        // println \"No key store defined. Signed release not available...\"\n    }\n\n    defaultConfig {\n        applicationId \"com.nononsenseapps.notepad\"\n        targetSdkVersion 36\n\n        // most people have at least android 7.1 anyway: https://apilevels.com/\n        // 93% of users are on API>=27 => don't write special code for 23<=API<=26, it's worthless\n        minSdkVersion 23\n\n        // If these values remain static, incremental builds are optimized and faster.\n        // Also, FDROID is happier. Update them only with ./release.sh\n        versionCode 72600\n        versionName \"7.2.6\"\n\n        vectorDrawables.useSupportLibrary = true\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        testInstrumentationRunnerArguments disableAnalytics: 'true'\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                // you need these to make org.androidannotations:androidannotations compile\n                // in \"playStore\" build type.\n                // TODO get rid of androidannotations and delete this javaCompileOptions {...}\n                arguments = [\n                        \"resourcePackageName\": android.defaultConfig.applicationId,\n                        \"androidManifestFile\": \"$projectDir/src/main/AndroidManifest.xml\".toString()\n                ]\n            }\n        }\n    }\n\n    // disable code shrinking in debug, enable it in release mode\n    buildTypes {\n        debug {\n            // identify the debug version to allow installing it\n            // alongside the fdroid or play store version\n            applicationIdSuffix \".debug\"\n            versionNameSuffix \"-Debug\"\n\n            minifyEnabled = false\n            shrinkResources = false\n            pseudoLocalesEnabled = true\n            // TODO test https://developer.android.com/guide/topics/resources/pseudolocales\n            // just to make gradle shut up about missing classes\n            proguardFiles 'proguard.pro'\n\n            // to install the play store and fdroid versions simultaneously, content provider\n            // authorities must be unique => define one for xml files\n            resValue \"string\", \"NnnAuthority_1\", defaultConfig.applicationId + applicationIdSuffix + \".MyContentAuthority\"\n        }\n        release {\n            // the FDROID release, where we CAN'T use applicationIdSuffix or versionNameSuffix\n\n            // enable R8\n            minifyEnabled true\n            shrinkResources = true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard.pro'\n            if (project.hasProperty('STORE_FILE')) {\n                signingConfig = signingConfigs.release\n            }\n\n            /// to install the play store and fdroid versions simultaneously, content provider\n            // authorities must be unique => define one for xml and for java\n            resValue \"string\", \"NnnAuthority_1\", defaultConfig.applicationId + \".MyContentAuthority\"\n        }\n        playStore {\n            // a RELEASE build for the google play store. The ONLY difference is the package name,\n            // which lets you install this app alongside the version from f-droid\n            applicationIdSuffix \".play\"\n            versionNameSuffix \"-Play\"\n            // enable R8\n            minifyEnabled true\n            shrinkResources = true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard.pro'\n            if (project.hasProperty('STORE_FILE')) {\n                signingConfig = signingConfigs.release\n            } else {\n                println \"STORE_FILE property is not set: cannot sign release build for the play store!\"\n            }\n            // This is a sorted list of fallback build types used when a dependency does not\n            // include a \"playStore\" build type. The plugin selects the first build type available\n            // in the dependency. So this will use the \"release\" mode of drag-sort-listview for\n            // our \"playStore\" profile\n            matchingFallbacks = ['release', 'debug']\n\n            // to install the play store and fdroid versions simultaneously, content provider\n            // authorities must be unique => define one for xml and for java\n            resValue \"string\", \"NnnAuthority_1\", defaultConfig.applicationId + applicationIdSuffix + \".MyContentAuthority\"\n        }\n    }\n\n    testOptions {\n        // androidx espresso tests require this\n        animationsDisabled = true\n    }\n\n    compileOptions {\n        // enable support for new language APIs in old devices\n        coreLibraryDesugaringEnabled = true\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n\n    // kotlinOptions { jvmTarget = 17 } // compile kotlin to the same JAVA 17\n\n    // control which types of configuration APKs you want your\n    // app bundle to support\n    bundle {\n        language {\n            // download ALL languages, because we have\n            // a built-in language picker\n            enableSplit = false\n        }\n    }\n}\n\ndependencies {\n    // AndroidX\n    implementation \"androidx.cursoradapter:cursoradapter:1.0.0\"\n    implementation 'androidx.fragment:fragment:1.8.8'\n    implementation 'androidx.appcompat:appcompat:1.7.1'\n    implementation \"androidx.preference:preference:1.2.1\"\n    implementation \"androidx.startup:startup-runtime:1.2.0\"\n    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'\n    // the logcat complained once because we didn't have this\n    implementation 'androidx.tracing:tracing:1.3.0'\n    // pretty widgets\n    implementation 'com.google.android.material:material:1.12.0'\n    // Time library, open source & up to date\n    implementation 'joda-time:joda-time:2.14.0'\n    // Dashclock API, open source & abandoned, but it still works\n    implementation 'com.google.android.apps.dashclock:dashclock-api:2.0.0'\n    // Writes org files, open source & abandoned, but it still works\n    implementation 'org.cowboyprogrammer.orgparser:orgparser:1.3.1'\n    // for manual list sorting, local source copy, open source & abandoned\n    implementation project(':external:drag-sort-listview')\n    // for highlighting functionality on 1° launch, open source & up to date\n    implementation 'com.getkeepsafe.taptargetview:taptargetview:1.15.0'\n    // from deleted branch 'version6'\n    implementation project(path: ':contract')\n    // for ListenableFuture<>\n    implementation 'com.google.guava:guava:33.4.8-android'\n    // annotations library, open source & abandoned\n    annotationProcessor \"org.androidannotations:androidannotations:4.8.0\"\n    implementation \"org.androidannotations:androidannotations-api:4.8.0\"\n    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'\n    // Tests libraries\n    androidTestImplementation 'androidx.test.ext:junit:1.3.0'\n    androidTestImplementation 'androidx.test:rules:1.7.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.7.0'\n    androidTestImplementation \"androidx.test.espresso:espresso-intents:3.7.0\"\n    androidTestUtil \"androidx.test.services:test-services:1.6.0\"\n    // no kotlin!\n    // implementation \"org.jetbrains.kotlin:kotlin-stdlib:1.7.20\"\n}\n"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DBItem.java",
    "content": "package com.nononsenseapps.notepad.database;\n\nimport android.content.Context;\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.net.Uri;\n\npublic abstract class DBItem {\n\tpublic static final String COL_ID = \"_id\";\n\n\tpublic DBItem() {}\n\n\tpublic DBItem(final Cursor cursor) {}\n\n\tpublic abstract ContentValues getContent();\n\n\tpublic abstract String getTableName();\n\n\tpublic abstract long getId();\n\n\tpublic abstract void setId(final long id);\n\n\tpublic abstract String[] getFields();\n\n\tpublic Uri getUri() {\n\t\treturn Uri.withAppendedPath(getBaseUri(), Long.toString(getId()));\n\t}\n\n\tpublic Uri getBaseUri() {\n\t\treturn Uri.withAppendedPath(\n\t\t\t\tUri.parse(ItemProvider.SCHEME\n\t\t\t\t\t\t+ ItemProvider.AUTHORITY), getTableName());\n\t}\n\n\tpublic void notifyProvider(final Context context) {\n\t\ttry {\n\t\t\tcontext.getContentResolver().notifyChange(getUri(), null, false);\n\t\t} catch (UnsupportedOperationException e) {\n\t\t\t// Catch this for test suite. Mock provider cant notify\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DatabaseHandler.java",
    "content": "package com.nononsenseapps.notepad.database;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\n/**\n * Database handler, SQLite wrapper and ORM layer.\n */\npublic class DatabaseHandler extends SQLiteOpenHelper {\n\n\t// All Static variables\n\t// Database Version\n\tprivate static final int DATABASE_VERSION = 1;\n\n\t// Database Name\n\tprivate static final String DATABASE_NAME = \"SampleDB\";\n\tprivate final Context context;\n\n\tprivate static DatabaseHandler instance = null;\n\n\tpublic synchronized static DatabaseHandler getInstance(Context context) {\n\t\tif (instance == null)\n\t\t\tinstance = new DatabaseHandler(context.getApplicationContext());\n\t\treturn instance;\n\t}\n\n\tpublic DatabaseHandler(Context context) {\n\t\tsuper(context.getApplicationContext(), DATABASE_NAME, null,\n\t\t\t\tDATABASE_VERSION);\n\t\tthis.context = context.getApplicationContext();\n\t}\n\n\t@Override\n\tpublic void onOpen(SQLiteDatabase db) {\n\t\tsuper.onOpen(db);\n\t\tif (!db.isReadOnly()) {\n\t\t\t// Enable foreign key constraints\n\t\t\t// This line requires android16\n\t\t\t// db.setForeignKeyConstraintsEnabled(true);\n\t\t\t// This line works everywhere though\n\t\t\tdb.execSQL(\"PRAGMA foreign_keys=ON;\");\n\n\t\t\t// Create temporary triggers and views\n\t\t\tDatabaseTriggers.createTemp(db);\n\t\t\tDatabaseViews.createTemp(db);\n\t\t}\n\t}\n\n\t@Override\n\tpublic synchronized void onCreate(SQLiteDatabase db) {\n\n\t\tdb.execSQL(\"DROP TABLE IF EXISTS \" + taskviewItem.TABLE_NAME);\n\t\tdb.execSQL(taskviewItem.CREATE_TABLE);\n\n\n\t\t// Create Triggers and Views\n\t\tDatabaseTriggers.create(db);\n\t\tDatabaseViews.create(db);\n\t}\n\n\t// Upgrading database\n\t@Override\n\tpublic synchronized void onUpgrade(SQLiteDatabase db, int oldVersion,\n\t\t\t\t\t\t\t\t\t   int newVersion) {\n\t\t// Try to drop and recreate. You should do something clever here\n\t\tonCreate(db);\n\t}\n\n\t// Convenience methods\n\tpublic synchronized boolean putItem(final DBItem item) {\n\t\tboolean success = false;\n\t\tint result = 0;\n\t\tfinal SQLiteDatabase db = this.getWritableDatabase();\n\t\tfinal ContentValues values = item.getContent();\n\n\t\tif (item.getId() > -1) {\n\t\t\tresult += db.update(item.getTableName(), values,\n\t\t\t\t\tDBItem.COL_ID + \" IS ?\",\n\t\t\t\t\tnew String[] { String.valueOf(item.getId()) });\n\t\t}\n\n\t\t// Update failed or wasn't possible, insert instead\n\t\tif (result < 1) {\n\t\t\tfinal long id = db.insert(item.getTableName(), null, values);\n\n\t\t\tif (id > 0) {\n\t\t\t\titem.setId(id);\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t} else {\n\t\t\tsuccess = true;\n\t\t}\n\n\t\tif (success) {\n\t\t\titem.notifyProvider(context);\n\t\t}\n\t\treturn success;\n\t}\n\n\tpublic synchronized int deleteItem(DBItem item) {\n\t\tfinal SQLiteDatabase db = this.getWritableDatabase();\n\t\tfinal int result = db.delete(item.getTableName(), DBItem.COL_ID\n\t\t\t\t+ \" IS ?\", new String[] { Long.toString(item.getId()) });\n\n\t\tif (result > 0) {\n\t\t\titem.notifyProvider(context);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\n\tpublic synchronized Cursor gettaskviewItemCursor(final long id) {\n\t\tfinal SQLiteDatabase db = this.getReadableDatabase();\n\t\tfinal Cursor cursor = db.query(taskviewItem.TABLE_NAME,\n\t\t\t\ttaskviewItem.FIELDS, taskviewItem.COL_ID + \" IS ?\",\n\t\t\t\tnew String[] { String.valueOf(id) }, null, null, null, null);\n\t\treturn cursor;\n\t}\n\n\tpublic synchronized taskviewItem gettaskviewItem(final long id) {\n\t\tfinal Cursor cursor = gettaskviewItemCursor(id);\n\t\tfinal taskviewItem result;\n\t\tif (cursor.moveToFirst()) {\n\t\t\tresult = new taskviewItem(cursor);\n\t\t} else {\n\t\t\tresult = null;\n\t\t}\n\n\t\tcursor.close();\n\t\treturn result;\n\t}\n\n\tpublic synchronized Cursor getAlltaskviewItemsCursor(final String selection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t final String[] args,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t final String sortOrder) {\n\t\tfinal SQLiteDatabase db = this.getReadableDatabase();\n\n\t\tfinal Cursor cursor = db.query(taskviewItem.TABLE_NAME,\n\t\t\t\ttaskviewItem.FIELDS, selection, args, null, null, sortOrder, null);\n\n\t\treturn cursor;\n\t}\n\n\tpublic synchronized List<taskviewItem> getAlltaskviewItems(final String selection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   final String[] args,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   final String sortOrder) {\n\t\tfinal List<taskviewItem> result = new ArrayList<taskviewItem>();\n\n\t\tfinal Cursor cursor = getAlltaskviewItemsCursor(selection, args, sortOrder);\n\n\t\twhile (cursor.moveToNext()) {\n\t\t\ttaskviewItem q = new taskviewItem(cursor);\n\t\t\tresult.add(q);\n\t\t}\n\n\t\tcursor.close();\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DatabaseTriggers.java",
    "content": "package com.nononsenseapps.notepad.database;\n\nimport android.database.sqlite.SQLiteDatabase;\n\npublic class DatabaseTriggers {\n\n\t/**\n\t * Create permanent triggers. They are dropped first,\n\t * if they already exist.\n\t */\n\tpublic static void create(final SQLiteDatabase db) {\n\n\t}\n\n\t/**\n\t * Create temporary triggers. Nothing is done if they\n\t * already exist.\n\t */\n\tpublic static void createTemp(final SQLiteDatabase db) {\n\n\t}\n\n\n}"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DatabaseViews.java",
    "content": "package com.nononsenseapps.notepad.database;\n\nimport android.database.sqlite.SQLiteDatabase;\n\npublic class DatabaseViews {\n\n\t/**\n\t * Create permanent views. They are dropped first,\n\t * if they already exist.\n\t */\n\tpublic static void create(final SQLiteDatabase db) {\n\n\t}\n\n\t/**\n\t * Create temporary views. Nothing is done if they\n\t * already exist.\n\t */\n\tpublic static void createTemp(final SQLiteDatabase db) {\n\t\tdb.execSQL(taskview_template);\n\t}\n\n\tprivate static final String taskview_template =\n\t\t\t\"CREATE TEMP VIEW IF NOT EXISTS taskview_template AS SELECT _id FROM taskfts WHERE ftstable MATCH 'textfilter';\";\n}"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/ItemProvider.java",
    "content": "package com.nononsenseapps.notepad.database;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\n\npublic class ItemProvider extends ContentProvider {\n\tpublic static final String AUTHORITY = \"com.nononsenseapps.notepad.database.AUTHORITY\";\n\tpublic static final String SCHEME = \"content://\";\n\n\tprivate static final UriMatcher sURIMatcher = new UriMatcher(\n\t\t\tUriMatcher.NO_MATCH);\n\n\tstatic {\n\t\ttaskviewItem.addMatcherUris(sURIMatcher);\n\t}\n\n\t@Override\n\tpublic boolean onCreate() {\n\t\treturn true;\n\t}\n\n\n\t@Override\n\tpublic int delete(Uri uri, String selection, String[] selectionArgs) {\n\t\t// Setup some common parsing and stuff\n\t\tfinal String table;\n\t\tfinal ContentValues values = new ContentValues();\n\t\tfinal ArrayList<String> args = new ArrayList<String>();\n\t\tif (selectionArgs != null) {\n\t\t\tfor (String arg : selectionArgs) {\n\t\t\t\targs.add(arg);\n\t\t\t}\n\t\t}\n\t\tfinal StringBuilder sb = new StringBuilder();\n\t\tif (selection != null && !selection.isEmpty()) {\n\t\t\tsb.append(\"(\").append(selection).append(\")\");\n\t\t}\n\n\t\t// Configure table and args depending on uri\n\t\tswitch (sURIMatcher.match(uri)) {\n\n\t\t\tcase taskviewItem.BASEITEMCODE:\n\t\t\t\ttable = taskviewItem.TABLE_NAME;\n\t\t\t\tif (selection != null && !selection.isEmpty()) {\n\t\t\t\t\tsb.append(\" AND \");\n\t\t\t\t}\n\t\t\t\tsb.append(taskviewItem.COL_ID + \" IS ?\");\n\t\t\t\targs.add(uri.getLastPathSegment());\n\t\t\t\t// Alternative is this\n\t\t\t\t// values.put(taskviewItem.COL_DELETED, 1);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown URI \" + uri);\n\t\t}\n\n\t\t// Write to DB\n\t\tfinal SQLiteDatabase db = DatabaseHandler.getInstance(getContext())\n\t\t\t\t.getWritableDatabase();\n\t\tfinal String[] argArray = new String[args.size()];\n\t\tfinal int result = db.delete(table, sb.toString(),\n\t\t\t\targs.toArray(argArray));\n\t\t// Or alternatively\n\t\t//final int result = db.update(table, values, sb.toString(),\n\t\t//        args.toArray(argArray));\n\n\t\tif (result > 0) {\n\t\t\t// Support upload sync\n\t\t\tgetContext().getContentResolver().notifyChange(uri, null, true);\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic Uri insert(Uri uri, ContentValues values) {\n\t\t// TODO: Implement this to handle requests to insert a new row.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic int update(Uri uri, ContentValues values, String selection,\n\t\t\t\t\t  String[] selectionArgs) {\n\t\t// TODO: Implement this to handle requests to update one or more rows.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic String getType(Uri uri) {\n\t\tswitch (sURIMatcher.match(uri)) {\n\n\t\t\tcase taskviewItem.BASEITEMCODE:\n\t\t\t\treturn taskviewItem.TYPE_ITEM;\n\t\t\tcase taskviewItem.BASEURICODE:\n\t\t\t\treturn taskviewItem.TYPE_DIR;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown URI \" + uri);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Cursor query(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\tString[] args, String sortOrder) {\n\t\tCursor result = null;\n\t\tfinal long id;\n\t\tfinal DatabaseHandler handler = DatabaseHandler.getInstance(getContext());\n\n\t\tswitch (sURIMatcher.match(uri)) {\n\n\t\t\tcase taskviewItem.BASEITEMCODE:\n\t\t\t\tid = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult = handler.gettaskviewItemCursor(id);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase taskviewItem.BASEURICODE:\n\t\t\t\tresult = handler.getAlltaskviewItemsCursor(selection, args, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown URI \" + uri);\n\t\t}\n\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/taskviewItem.java",
    "content": "package com.nononsenseapps.notepad.database;\n\nimport android.content.ContentValues;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\n\n/**\n * Represents taskview in the database.\n */\npublic class taskviewItem extends DBItem {\n\tpublic static final String TABLE_NAME = \"taskview\";\n\n\tpublic static Uri URI() {\n\t\treturn Uri.withAppendedPath(\n\t\t\t\tUri.parse(ItemProvider.SCHEME\n\t\t\t\t\t\t+ ItemProvider.AUTHORITY), TABLE_NAME);\n\t}\n\n\t// Column names\n\tpublic static final String COL_ID = \"_id\";\n\tpublic static final String COL_TITLE = \"title\";\n\tpublic static final String COL_FILTERTEXT = \"filterText\";\n\tpublic static final String COL_FILTERDUEEARLIEST = \"filterDueEarliest\";\n\tpublic static final String COL_FILTERDUELATEST = \"filterDueLatest\";\n\tpublic static final String COL_FILTERCOMPLETED = \"filterCompleted\";\n\n\t// For database projection so order is consistent\n\tpublic static final String[] FIELDS = { COL_ID, COL_TITLE, COL_FILTERTEXT, COL_FILTERDUEEARLIEST, COL_FILTERDUELATEST, COL_FILTERCOMPLETED };\n\n\tpublic Long _id = -1;\n\tpublic String title = \"\";\n\tpublic String filterText = \"\";\n\tpublic Long filterDueEarliest;\n\tpublic Long filterDueLatest;\n\tpublic Long filterCompleted;\n\n\tpublic static final int BASEURICODE = 0x2d72bbd;\n\tpublic static final int BASEITEMCODE = 0x824cd6d;\n\n\tpublic static void addMatcherUris(UriMatcher sURIMatcher) {\n\t\tsURIMatcher.addURI(ItemProvider.AUTHORITY, TABLE_NAME, BASEURICODE);\n\t\tsURIMatcher.addURI(ItemProvider.AUTHORITY, TABLE_NAME + \"/#\", BASEITEMCODE);\n\t}\n\n\tpublic static final String TYPE_DIR = \"vnd.android.cursor.dir/vnd.com.nononsenseapps.notepad.database.\" + TABLE_NAME;\n\tpublic static final String TYPE_ITEM = \"vnd.android.cursor.item/vnd.com.nononsenseapps.notepad.database.\" + TABLE_NAME;\n\n\tpublic taskviewItem() {\n\t\tsuper();\n\t}\n\n\tpublic taskviewItem(final Cursor cursor) {\n\t\tsuper();\n\t\t// Projection expected to match FIELDS array\n\t\tthis._id = cursor.getLong(0);\n\t\tthis.title = cursor.getString(1);\n\t\tthis.filterText = cursor.getString(2);\n\t\tthis.filterDueEarliest = cursor.isNull(3) ? null : cursor.getLong(3);\n\t\tthis.filterDueLatest = cursor.isNull(4) ? null : cursor.getLong(4);\n\t\tthis.filterCompleted = cursor.isNull(5) ? null : cursor.getLong(5);\n\t}\n\n\tpublic ContentValues getContent() {\n\t\tContentValues values = new ContentValues();\n\n\t\tvalues.put(COL_TITLE, title);\n\t\tvalues.put(COL_FILTERTEXT, filterText);\n\t\tif (filterDueEarliest != null) {\n\t\t\tvalues.put(COL_FILTERDUEEARLIEST, filterDueEarliest);\n\t\t} else {\n\t\t\tvalues.putNull(COL_FILTERDUEEARLIEST);\n\t\t}\n\t\tif (filterDueLatest != null) {\n\t\t\tvalues.put(COL_FILTERDUELATEST, filterDueLatest);\n\t\t} else {\n\t\t\tvalues.putNull(COL_FILTERDUELATEST);\n\t\t}\n\t\tif (filterCompleted != null) {\n\t\t\tvalues.put(COL_FILTERCOMPLETED, filterCompleted);\n\t\t} else {\n\t\t\tvalues.putNull(COL_FILTERCOMPLETED);\n\t\t}\n\n\t\treturn values;\n\t}\n\n\tpublic String getTableName() {\n\t\treturn TABLE_NAME;\n\t}\n\n\tpublic String[] getFields() {\n\t\treturn FIELDS;\n\t}\n\n\tpublic long getId() {\n\t\treturn _id;\n\t}\n\n\tpublic void setId(final long id) {\n\t\t_id = id;\n\t}\n\n\tpublic static final String CREATE_TABLE =\n\t\t\t\"CREATE TABLE taskview\"\n\t\t\t\t\t+ \"  (_id INTEGER PRIMARY KEY,\"\n\t\t\t\t\t+ \"  title TEXT NOT NULL DEFAULT '',\"\n\t\t\t\t\t+ \"  filterText TEXT NOT NULL DEFAULT '',\"\n\t\t\t\t\t+ \"  filterDueEarliest INTEGER,\"\n\t\t\t\t\t+ \"  filterDueLatest INTEGER,\"\n\t\t\t\t\t+ \"  filterCompleted INTEGER\"\n\t\t\t\t\t+ \"\"\n\t\t\t\t\t+ \"  )\";\n}\n"
  },
  {
    "path": "app/dbsetup.py",
    "content": "\"\"\"Generate a sample project with triggers\"\"\"\n\nfrom AndroidCodeGenerator.generator import Generator\nfrom AndroidCodeGenerator.sql_validator import SQLTester\nfrom AndroidCodeGenerator.db_table import (Table, Column, ForeignKey, Unique,\n                                           Trigger, Check)\nfrom AndroidCodeGenerator.database_triggers import DatabaseTriggers\n\ntables = []\ntriggers = []\n\ntasklists = Table('tasklist')\ntasklists.add_cols(Column('title').text.not_null.default(\"''\"),\n                   Column('updated').integer,\n                   Column('listtype').text,\n                   Column('sorting').text,\n                   Column('deleted').integer.not_null.default(0),\n                   # New fields\n                   Column('ctime').timestamp.default_current_timestamp,\n                   Column('mtime').timestamp.default_current_timestamp,\n                   # GTask fields\n                   Column('account').text,\n                   Column('remoteid').text)\ntasklists.add_constraints(Unique('account', 'remoteid').on_conflict_replace)\n\ntables.append(tasklists)\n\ntasks = Table('task')\ntasks.add_cols(Column('title').text.not_null.default(\"''\"),\n               Column('note').text.not_null.default(\"''\"),\n               Column('completed').integer,\n               Column('updated').integer,\n               Column('due').integer,\n               Column('locked').integer.not_null.default(0),\n               Column('deleted').integer.not_null.default(0),\n               # Positions\n               Column('posleft').integer.not_null.default(1),\n               Column('posright').integer.not_null.default(2),\n               Column('tasklist').integer.not_null,\n               # New fields\n               Column('ctime').timestamp.default_current_timestamp,\n               Column('mtime').timestamp.default_current_timestamp,\n               # GTask fields\n               Column('account').text,\n               Column('remoteid').text)\n\n\ntasks.add_constraints(ForeignKey('tasklist').references(tasklists.name)\\\n                      .on_delete_cascade,\n                      Unique('account', 'remoteid').on_conflict_replace,\n                      Check('posleft', '> 0'),\n                      Check('posright', '> 1'))\n\ntables.append(tasks)\n\nlog = Table('history')\nlog.add_cols(Column('taskid').integer,\n             Column('title').text.not_null.default(\"''\"),\n             Column('note').text.not_null.default(\"''\"),\n             Column('deleted').integer.not_null.default(0),\n             Column('updated').timestamp.not_null.default_current_timestamp)\nlog.add_constraints(ForeignKey('taskid').references(tasks.name)\\\n                    .on_delete_set_null)\n\ntables.append(log)\n\nfor name in ['tr_up_history', 'tr_ins_history',\n             'tr_del_history']:\n    t = Trigger(name).temp.if_not_exists\n    deleted = 'new.deleted'\n    if 'up' in name:\n        t.after.update_on(tasks.name)\n    elif 'ins' in name:\n        t.after.insert_on(tasks.name)\n    else:\n        t.before.delete_on(tasks.name)\n        deleted = 1\n    t.do_sql(\"INSERT INTO {} \\\n(taskid, title, note, deleted) \\\nVALUES (new._id, new.title, new.note, {})\".format(log.name, deleted))\n    triggers.append(t)\n\n# Notifications\nnots = Table('notification')\nnots.add_cols(Column('time').integer,\n             Column('permanent').integer.not_null.default(0),\n             Column('taskid').integer,\n             Column('repeats').integer.not_null.default(0),\n             Column('locationname').text,\n             Column('latitude').real,\n             Column('longitude').real,\n             Column('radius').real)\nnots.add_constraints(ForeignKey('taskid').references(tasks.name)\\\n                    .on_delete_cascade)\ntables.append(nots)\n\n# Let's try to create the SQL\nst = SQLTester()\nst.add_tables(*tables)\nst.add_triggers(*triggers)\n\nst.test_create()\n\n#g = Generator(path='./sample/src/com/example/appname/database/')\n#g.add_tables(persons, log)\n#g.add_triggers(trigger)\n#g.write()\n"
  },
  {
    "path": "app/proguard.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# I only care about minimizing\n-dontobfuscate\n\n# Everything in the app is essential\n-keep class com.nononsenseapps.** { *; }\n-keep public class com.nononsenseapps.** { *; }\n-keep class com.mobeta.android.** { *; }\n\n-keepclassmembers enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n}\n\n# gradle (and stackoverflow) agree that suppressing the warning is appropriate\n-dontwarn org.joda.convert.**\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/BaseTestClass.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.isRoot;\n\nimport android.Manifest;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Build;\n\nimport androidx.preference.PreferenceManager;\nimport androidx.test.core.app.ApplicationProvider;\nimport androidx.test.espresso.intent.rule.IntentsTestRule;\nimport androidx.test.ext.junit.rules.ActivityScenarioRule;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.rule.GrantPermissionRule;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\nimport com.nononsenseapps.notepad.database.DatabaseHandler;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\n\npublic class BaseTestClass {\n\n\t/**\n\t * A JUnit {@link Rule @Rule} to launch your activity under test. This replaces\n\t * ActivityInstrumentationTestCase2. Rules are executed for each test method and will run\n\t * before any of your setup code in the @Before method. To get a reference to the activity\n\t * you can use: {@link IntentsTestRule#getActivity()}\n\t * <br/>\n\t * NOTE: the newer alternative, {@link ActivityScenarioRule}, <b>DOES NOT WORK</b>\n\t */\n\t@SuppressWarnings(\"deprecation\")\n\t@Rule\n\tpublic IntentsTestRule<ActivityMain_> mActRule;\n\n\n\t/**\n\t * Since API 33 we need permission for notifications\n\t */\n\t@Rule\n\tpublic GrantPermissionRule mNotifRule;\n\n\t/**\n\t * @return a string with the content of the given resourceId\n\t */\n\tpublic String getStringResource(int resourceId) {\n\t\treturn mActRule.getActivity().getString(resourceId);\n\t}\n\n\t@After\n\tpublic void clearAppData() {\n\t\tif (mActRule != null) mActRule.finishActivity();\n\t\tContext context = ApplicationProvider.getApplicationContext();\n\n\t\t// clear the app's data as the test is starting & finishing\n\t\tPreferenceManager.getDefaultSharedPreferences(context).edit().clear().commit();\n\t\tDatabaseHandler.resetDatabase(context);\n\t}\n\n\t/**\n\t * Tries in many ways to give notification permission for OS versions that need it\n\t */\n\tprivate void giveNotifyPermission() {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n\t\t\t// this permission works only on API >= 33, it crashes on older versions!\n\t\t\tmNotifRule = GrantPermissionRule.grant(Manifest.permission.POST_NOTIFICATIONS);\n\n\t\t\tString command = \"pm grant \" +\n\t\t\t\t\tApplicationProvider.getApplicationContext().getPackageName() + \" \" +\n\t\t\t\t\tManifest.permission.POST_NOTIFICATIONS;\n\n\t\t\t// this one is more likely to work\n\t\t\tInstrumentationRegistry\n\t\t\t\t\t.getInstrumentation()\n\t\t\t\t\t.getUiAutomation()\n\t\t\t\t\t.executeShellCommand(command);\n\t\t} else {\n\t\t\tmNotifRule = null;\n\t\t}\n\t}\n\n\t/**\n\t * Many times, on the github VM, the tests fail with RootViewWithoutFocusException,\n\t * I think it's due to the emulator being slow. Let's launch the activity and wait\n\t * for it to load before starting the real test\n\t */\n\t@Before\n\tpublic void launchAndWait() {\n\t\t// ensure that this is called BEFORE trying to start the activity\n\t\tclearAppData();\n\n\t\t// first, acquire all the required permissions ...\n\t\tgiveNotifyPermission();\n\n\t\t// ... then, create and run the entry point to the app\n\t\tmActRule = new IntentsTestRule<>(ActivityMain_.class);\n\t\tIntent launchApp = new Intent(ApplicationProvider.getApplicationContext(), ActivityMain_.class);\n\t\tmActRule.launchActivity(launchApp);\n\n\t\ttry {\n\t\t\t// it responds => we can return now\n\t\t\tonView(isRoot()).check(matches(isDisplayed()));\n\t\t\treturn;\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.error(this.getClass(), \"Activity isn't responsive:\");\n\t\t\tNnnLogger.exception(e);\n\t\t}\n\n\t\t// maybe we just have to wait\n\t\tEspressoHelper.waitUi();\n\n\t\t// if it's still not enough, let's crash here\n\t\ttry {\n\t\t\tonView(isRoot()).check(matches(isDisplayed()));\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.error(this.getClass(), \"Can't launch activity:\");\n\t\t\tNnnLogger.exception(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/EspressoHelper.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;\nimport static androidx.test.espresso.action.ViewActions.typeText;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.RootMatchers.isDialog;\nimport static androidx.test.espresso.matcher.ViewMatchers.isClickable;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.isRoot;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.instanceOf;\n\nimport android.app.UiAutomation;\nimport android.os.SystemClock;\n\nimport androidx.annotation.IdRes;\nimport androidx.test.espresso.AmbiguousViewMatcherException;\nimport androidx.test.espresso.Espresso;\nimport androidx.test.espresso.ViewInteraction;\nimport androidx.test.espresso.contrib.DrawerActions;\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.getkeepsafe.taptargetview.TapTargetView;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain;\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Assert;\n\npublic class EspressoHelper {\n\n\t/**\n\t * Wait for 350ms to work around timing issues on slow emulators. It's called to solve issues\n\t * with flaky tests on github runners. Sometime tests need it, sometimes they don't,\n\t * but you can't know. It should go after every call to {@link ViewInteraction#perform}\n\t */\n\tpublic static void waitUi() {\n\t\tInstrumentationRegistry.getInstrumentation().waitForIdleSync();\n\t\tSystemClock.sleep(400);\n\t}\n\n\t/**\n\t * open the drawer on the left\n\t */\n\tpublic static void openDrawer() {\n\t\ttry {\n\t\t\tonView(withId(R.id.drawerLayout)).check(matches(isDisplayed()));\n\t\t} catch (Exception e) {\n\t\t\tAssert.fail(\"Can't find the drawerLayout, maybe a dialog is still open?\");\n\t\t\treturn;\n\t\t}\n\t\tonView(withId(R.id.drawerLayout)).perform(DrawerActions.open());\n\t}\n\n\tpublic static void createNoteWithName(String noteName) {\n\t\tonView(withId(R.id.menu_add))\n\t\t\t\t.check(matches(isDisplayed()))\n\t\t\t\t.check(matches(isClickable()));\n\t\tonView(withId(R.id.menu_add)).perform(click());\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\t\tonView(withId(R.id.taskText)).perform(typeText(noteName));\n\t}\n\n\t/**\n\t * Presses \"+\" and writes text for each note given in noteNames\n\t */\n\tpublic static void createNotes(String[] noteNames) {\n\t\tfor (String noteName : noteNames) {\n\t\t\tcreateNoteWithName(noteName);\n\t\t}\n\t}\n\n\t/**\n\t * Add a new task list. The drawer should be open when this is called\n\t *\n\t * @param taskListName name of the task list\n\t */\n\tpublic static void createTaskList(String taskListName) {\n\t\tEspressoHelper.openDrawer();\n\n\t\t// dismiss the other showcase view\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tonView(withId(R.id.drawer_menu_createlist)).check(matches(isDisplayed()));\n\t\t// sometimes the automator will hold the button too long. This will show the tooltip,\n\t\t// and the test will fail because the button did not get the click to show the dialog.\n\t\t// It's a matter of luck: retry the test and it will work\n\t\tonView(withId(R.id.drawer_menu_createlist)).perform(click());\n\t\twaitUi(); // the popup may need time to load\n\n\t\t// the text field in the dialog visibile must be visible\n\t\tonViewWithIdInDialog(R.id.titleField).check(matches(isDisplayed()));\n\n\t\t// fill the popup\n\t\tonViewWithIdInDialog(R.id.titleField).perform(typeText(taskListName));\n\t\tonViewWithIdInDialog(R.id.dialog_yes).check(matches(isDisplayed()));\n\t\tonViewWithIdInDialog(R.id.dialog_yes).perform(closeSoftKeyboard());\n\t\tonViewWithIdInDialog(R.id.dialog_yes).perform(click());\n\t}\n\n\t/**\n\t * shorthand for onView(withId(viewId)).inRoot(isDialog())\n\t *\n\t * @param viewId it's R.id.something\n\t */\n\tpublic static ViewInteraction onViewWithIdInDialog(@IdRes int viewId) {\n\t\treturn onView(withId(viewId)).inRoot(isDialog());\n\t}\n\n\t/**\n\t * @return TRUE if the app is in tablet mode, FALSE in phone mode\n\t */\n\tpublic static boolean isInTabletMode() {\n\t\tboolean isInPortraitMode = InstrumentationRegistry\n\t\t\t\t.getInstrumentation()\n\t\t\t\t.getTargetContext()\n\t\t\t\t.getResources()\n\t\t\t\t.getBoolean(R.bool.fillEditor);\n\t\treturn !isInPortraitMode;\n\t}\n\n\tpublic static void navigateUp() {\n\t\tonView(isRoot()).perform(closeSoftKeyboard());\n\t\tif (isInTabletMode()) {\n\t\t\t// we are in tablet mode: press \"+\" to make a note appear in the list\n\t\t\tonView(withId(R.id.menu_add)).perform(click());\n\t\t} else {\n\t\t\t// we are in phone mode: close the keyboard & press the back button\n\t\t\tEspresso.pressBack();\n\t\t}\n\t}\n\n\t/**\n\t * Exits the \"settings\" activity, going back to {@link ActivityMain}\n\t */\n\tpublic static void exitPrefsActivity() {\n\n\t\tString label = InstrumentationRegistry\n\t\t\t\t.getInstrumentation()\n\t\t\t\t.getTargetContext()\n\t\t\t\t.getString(R.string.menu_preferences);\n\t\ttry {\n\t\t\t// TODO improve & return if necessary\n\t\t\tonView(withText(label)).check(matches(isDisplayed()));\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.warning(EspressoHelper.class, \"Can't determine if PrefsActivity is shown:\");\n\t\t\tNnnLogger.exception(e);\n\t\t}\n\n\t\t// for now, assume this function is called only when a fragment of PrefsActivity is shown\n\n\t\tif (isInTabletMode()) {\n\t\t\t// tablets show menu & category => press back only once\n\t\t\tEspresso.pressBack();\n\t\t} else {\n\t\t\t// in phones, go back to the menu, then back to ActivityMain\n\t\t\tEspresso.pressBack();\n\t\t\tEspresso.pressBack();\n\t\t}\n\t}\n\n\t/**\n\t * @return TRUE if the {@link TapTargetView} is currently shown, FALSE otherwise\n\t */\n\tprivate static Boolean isShowCaseOverlayVisible() {\n\t\ttry {\n\t\t\tonView(instanceOf(TapTargetView.class)).check(matches(isDisplayed()));\n\t\t\treturn true;\n\t\t} catch (AmbiguousViewMatcherException ignored) {\n\t\t\t// we have at least one, so it counts as visible\n\t\t\treturn true;\n\t\t} catch (Throwable ignored) {\n\t\t\t// it has to be \"Throwable\", not \"Exception\"\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * If the {@link TapTargetView} is shown, touch the screen to hide it, so that tests\n\t * can then interact with the app.\n\t */\n\tpublic static void hideShowCaseViewIfShown() {\n\t\twaitUi();\n\t\tif (!EspressoHelper.isShowCaseOverlayVisible()) return;\n\n\t\t// click anywhere to dismiss it\n\t\ttry {\n\t\t\tonView(instanceOf(TapTargetView.class)).perform(click());\n\t\t} catch (Exception ignored) {\n\t\t\tAssert.fail(\"Could not dismiss the TapTargetView\");\n\t\t\treturn;\n\t\t}\n\t\twaitUi();\n\t}\n\n\t/**\n\t * Rotate the screen twice, waiting ~4 seconds for the animations to finish.\n\t * It automatically understands if the phone or tablet is \"naturally\" held in\n\t * landscape or portrait mode, so test should be done with the emulator's default\n\t * settings: phones in portrait mode and tablets in landscape mode\n\t */\n\tpublic static void rotateScreenAndWait() {\n\t\tvar uiAuto = InstrumentationRegistry\n\t\t\t\t.getInstrumentation()\n\t\t\t\t.getUiAutomation();\n\t\t// rotate it\n\t\tuiAuto.setRotation(UiAutomation.ROTATION_FREEZE_270);\n\t\t// wait 1s for the rotation to finish\n\t\twaitUi();\n\t\twaitUi();\n\n\t\t// rotating the screen sometimes makes the taptargetview appear in the wrong place.\n\t\t// I have no idea why. In any case, we have to close it now, or else the next\n\t\t// screen rotation will make the app crash. Yes it's incomprehensible, but now it works\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\t// rotate it more\n\t\tuiAuto.setRotation(UiAutomation.ROTATION_FREEZE_0);\n\t\twaitUi();\n\t\twaitUi();\n\n\t\t// unfreeze it and let it go back to its default state\n\t\tuiAuto.setRotation(UiAutomation.ROTATION_UNFREEZE);\n\t\twaitUi();\n\t\twaitUi();\n\t\twaitUi();\n\t}\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddBigNumberOfNotesScrollDownAndDeleteOne.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static android.view.View.FIND_VIEWS_WITH_TEXT;\nimport static androidx.test.espresso.Espresso.onData;\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.action.ViewActions.scrollTo;\nimport static androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static junit.framework.Assert.assertTrue;\nimport static org.hamcrest.Matchers.allOf;\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\nimport static org.hamcrest.Matchers.instanceOf;\n\nimport android.view.View;\nimport android.widget.ListView;\n\nimport androidx.test.espresso.ViewAssertion;\nimport androidx.test.espresso.matcher.CursorMatchers;\nimport androidx.test.filters.LargeTest;\n\nimport com.mobeta.android.dslv.DragSortListView;\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Test;\n\nimport java.util.ArrayList;\n\n@LargeTest\npublic class TestAddBigNumberOfNotesScrollDownAndDeleteOne extends BaseTestClass {\n\n\tprivate final String[] noteNameList = {\n\t\t\t\"prepare food\", \"take dogs out\", \"water plants\", \"sleep\",\n\t\t\t\"go for a jog\", \"do some work\", \"play with the dog\",\n\t\t\t\"work out\", \"do weird stuff\", \"read a book\", \"drink water\",\n\t\t\t\"write a book\", \"proofread the book\", \"publish the book\",\n\t\t\t\"ponder life\", \"build a house\", \"repair the house\", \"call contractor\",\n\t\t\t\"write another book\", \"scrap the book project\", \"start a blog\",\n\t\t\t\"  \", \"     \"\n\t};\n\n\t/**\n\t * credit to <a href=\"https://gist.github.com/chemouna/00b10369eb1d5b00401b\">Chemouna @ GitHub</a>\n\t */\n\tprivate static ViewAssertion doesNotHaveViewWithText(final String text) {\n\t\treturn (view, e) -> {\n\t\t\tif (!(view instanceof ListView rv)) {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\tArrayList<View> outviews = new ArrayList<>();\n\t\t\tfor (int index = 0; index < rv.getAdapter().getCount(); index++) {\n\t\t\t\trv\n\t\t\t\t\t\t//.findViewHolderForAdapterPosition(index)\n\t\t\t\t\t\t//.itemView\n\t\t\t\t\t\t.findViewsWithText(outviews, text, FIND_VIEWS_WITH_TEXT);\n\t\t\t\tif (!outviews.isEmpty()) break;\n\t\t\t}\n\t\t\tassertTrue(outviews.isEmpty());\n\t\t};\n\t}\n\n\t@Test\n\tpublic void testAddBigNumberOfNotesScrollDownAndDeleteOne() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tEspressoHelper.createNotes(noteNameList);\n\n\t\t// exit the note editing mode\n\t\tEspressoHelper.navigateUp();\n\n\t\t// click on the bottom-most note\n\t\tonData(CursorMatchers\n\t\t\t\t\t.withRowString(\"title\", equalToIgnoringCase(noteNameList[0])))\n\t\t\t\t.inAdapterView(allOf(\n\t\t\t\t\t\thasMinimumChildCount(1),\n\t\t\t\t\t\tinstanceOf(DragSortListView.class)))\n\t\t\t\t.perform(scrollTo())\n\t\t\t\t.perform(click());\n\n\t\t// delete the note\n\t\tonView(withId(R.id.menu_delete)).perform(click());\n\t\tEspressoHelper.waitUi();\n\t\tonView(withId(android.R.id.button1)).perform(click());\n\n\t\t// check that the 1° note added was deleted\n\t\tonView(allOf(withId(android.R.id.list), isDisplayed()))\n\t\t\t\t.check(doesNotHaveViewWithText(noteNameList[0]));\n\n\t\t// if the showcaseview is visible when closing the app, there will be a crash\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\t}\n\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteShouldShowNameInNotesScreen.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\n\nimport androidx.test.filters.LargeTest;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestAddNewNoteShouldShowNameInNotesScreen extends BaseTestClass {\n\n\tprivate String noteName1;\n\n\t@Before\n\tpublic void initStrings() {\n\t\tnoteName1 = \"prepare food\";\n\t}\n\n\t@Test\n\tpublic void testAddNewNoteShouldShowNameInNotesScreen() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tEspressoHelper.createNoteWithName(noteName1);\n\t\tEspressoHelper.navigateUp();\n\n\t\tonView(withText(equalToIgnoringCase(noteName1))).check(matches(isDisplayed()));\n\t}\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithDueDateCheckDateIsVisible.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static org.hamcrest.Matchers.allOf;\n\nimport androidx.test.filters.LargeTest;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestAddNewNoteWithDueDateCheckDateIsVisible extends BaseTestClass {\n\n\tprivate String noteName1;\n\n\t@Before\n\tpublic void initStrings() {\n\t\tnoteName1 = \"prepare food\";\n\n\t}\n\n\t@Test\n\tpublic void testAddNewNoteWithDueDateCheckDateIsVisible() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tEspressoHelper.createNoteWithName(noteName1);\n\t\tonView(withId(R.id.dueDateBox)).perform(click());\n\t\tonView(withId(android.R.id.button1)).perform(click());\n\n\t\tEspressoHelper.navigateUp();\n\n\t\t// target only the dateview in the note shown in the \"tasks\" list\n\t\tvar dateView = onView(allOf(withId(R.id.date), isCompletelyDisplayed()));\n\t\tdateView.check(matches(isDisplayed()));\n\t}\n}"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithReminderDateAndTime.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\nimport androidx.test.filters.LargeTest;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestAddNewNoteWithReminderDateAndTime extends BaseTestClass {\n\n\tprivate String noteName1;\n\n\t@Before\n\tpublic void initStrings() {\n\t\tnoteName1 = \"prepare food\";\n\t}\n\n\t@Test\n\tpublic void testAddNewNoteWithReminderDateAndTime() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tEspressoHelper.createNoteWithName(noteName1);\n\n\t\t//add reminder\n\t\tonView(withId(R.id.notificationAdd)).perform(click());\n\t\tEspressoHelper.waitUi();\n\n\t\t//add date\n\t\tonView(withId(R.id.notificationDate)).perform(click());\n\t\tEspressoHelper.waitUi();\n\t\tonView(withId(android.R.id.button1)).perform(click());\n\n\t\t//add time\n\t\tonView(withId(com.nononsenseapps.notepad.R.id.notificationTime)).perform(click());\n\t\tonView(withId(android.R.id.button1)).perform(click());\n\n\t\tEspressoHelper.navigateUp();\n\n\t\t//check that the date field is visible\n\t\tonView(withText(equalToIgnoringCase(noteName1))).perform(click());\n\t\tonView(withId(R.id.notificationDate)).check(matches(isDisplayed()));\n\n\t\t// maybe we should also check someting like\n\t\t// onView(withId(R.id.notificationDate)).check(matches(withText(\"november 10\")));\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNoteToTaskList.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.hasSibling;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\n\nimport androidx.test.filters.LargeTest;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestAddNoteToTaskList extends BaseTestClass {\n\n\tprivate String taskListName, noteName1;\n\n\t@Before\n\tpublic void initStrings() {\n\t\ttaskListName = \"a random task list\";\n\t\tnoteName1 = \"prepare food\";\n\t}\n\n\t@Test\n\tpublic void testAddNoteToTaskList() {\n\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\t\tEspressoHelper.createTaskList(taskListName);\n\n\t\t// make sure the correct task list is selected\n\t\tEspressoHelper.openDrawer();\n\t\tonView(allOf(withText(taskListName), withId(android.R.id.text1)))\n\t\t\t\t.check(matches(isDisplayed()));\n\t\tonView(allOf(withText(taskListName), withId(android.R.id.text1)))\n\t\t\t\t.perform(click());\n\t\tEspressoHelper.waitUi();\n\n\t\t// add the note\n\t\tEspressoHelper.createNoteWithName(noteName1);\n\t\tEspressoHelper.navigateUp();\n\n\t\t// make sure that the number of notes for the task list is actually 1\n\t\tEspressoHelper.openDrawer();\n\t\tonView(allOf(withText(taskListName), hasSibling(withText(\"1\"))))\n\t\t\t\t.check(matches(withText(taskListName)));\n\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNotesAndRotateScreen.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\nimport static org.hamcrest.Matchers.instanceOf;\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\nimport androidx.test.filters.LargeTest;\n\nimport com.nononsenseapps.ui.TitleNoteTextView;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n\n@LargeTest\npublic class TestAddNotesAndRotateScreen extends BaseTestClass {\n\n\tString noteName1, noteName2, noteName3, noteName4;\n\n\t@Before\n\tpublic void initStrings() {\n\t\tnoteName1 = \"prepare food\";\n\t\tnoteName2 = \"take dogs out\";\n\t\tnoteName3 = \"water plants\";\n\t\tnoteName4 = \"sleep\";\n\t}\n\n\t@Test\n\tpublic void testAddNotesAndRotateScreen() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tString[] noteNames = { noteName1, noteName2, noteName3, noteName4 };\n\n\t\tEspressoHelper.createNotes(noteNames);\n\t\tEspressoHelper.navigateUp();\n\n\t\tEspressoHelper.rotateScreenAndWait();\n\n\t\t// in case it's still there\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\t// check that textviews still show up.\n\t\tonView(allOf(\n\t\t\t\t\tinstanceOf(TitleNoteTextView.class),\n\t\t\t\t\twithText(equalToIgnoringCase(noteNames[0]))))\n\t\t\t\t.check(matches(isDisplayed()));\n\t\tonView(allOf(\n\t\t\t\t\tinstanceOf(TitleNoteTextView.class),\n\t\t\t\t\twithText(equalToIgnoringCase(noteNames[1]))))\n\t\t\t\t.check(matches(isDisplayed()));\n\t\tonView(allOf(\n\t\t\t\t\tinstanceOf(TitleNoteTextView.class),\n\t\t\t\t\twithText(equalToIgnoringCase(noteNames[2]))))\n\t\t\t\t.check(matches(isDisplayed()));\n\t\tonView(allOf(\n\t\t\t\t\tinstanceOf(TitleNoteTextView.class),\n\t\t\t\t\twithText(equalToIgnoringCase(noteNames[3]))))\n\t\t\t\t.check(matches(isDisplayed()));\n\t}\n}\n\n\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListAndRotateScreen.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\nimport static org.hamcrest.Matchers.allOf;\n\nimport androidx.test.espresso.contrib.RecyclerViewActions;\nimport androidx.test.filters.LargeTest;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestAddTaskListAndRotateScreen extends BaseTestClass {\n\n\tprivate String taskListName;\n\n\t@Before\n\tpublic void initStrings() {\n\t\ttaskListName = \"a random task list\";\n\t}\n\n\t@Test\n\tpublic void testAddTaskListAndRotateScreen() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\t\tEspressoHelper.createTaskList(taskListName);\n\n\t\tEspressoHelper.openDrawer();\n\n\t\tEspressoHelper.rotateScreenAndWait();\n\n\t\t// make sure the task list is still visible.\n\t\t// if the rotations didn't finish, it will crash here\n\t\tRecyclerViewActions\n\t\t\t\t.scrollTo(hasDescendant(withText(equalToIgnoringCase(taskListName))));\n\t\tonView(allOf(\n\t\t\t\t\twithText(equalToIgnoringCase(taskListName)),\n\t\t\t\t\twithId(android.R.id.text1)))\n\t\t\t\t.check(matches(isDisplayed()));\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListCheckItIsAddedToDrawer.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\n\nimport androidx.test.filters.LargeTest;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestAddTaskListCheckItIsAddedToDrawer extends BaseTestClass {\n\n\tprivate String taskListName;\n\n\t@Before\n\tpublic void initStrings() {\n\t\ttaskListName = \"a random task list\";\n\t}\n\n\t@Test\n\tpublic void testAddTaskListCheckItIsAddedToDrawer() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tEspressoHelper.createTaskList(taskListName);\n\t\tEspressoHelper.openDrawer();\n\n\t\t//check that the new note is found and has the correct text\n\t\tonView(allOf(withText(taskListName), withId(android.R.id.text1)))\n\t\t\t\t.check(matches(isDisplayed()));\n\t}\n\n\n}\n\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListsScrollNavigationDrawer.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onData;\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.action.ViewActions.scrollTo;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\n\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\n\nimport androidx.test.espresso.matcher.CursorMatchers;\nimport androidx.test.filters.LargeTest;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Test;\n\n@LargeTest\npublic class TestAddTaskListsScrollNavigationDrawer extends BaseTestClass {\n\n\tfinal String[] taskListNames = { \"Lorem\", \"ipsum \", \"dolor \", \"sit \", \"amet\", \"consectetur \",\n\t\t\t\"adipiscing \", \"elit\", \"sed \", \"do \", \"eiusmod \", \"tempor \", \"incididunt \",\n\t\t\t\"ut \", \"labore \" };\n\n\t@Test\n\tpublic void testAddTaskListsScrollNavigationDrawer() {\n\t\tString SETTINGS_TEXT = getStringResource(R.string.menu_preferences);\n\t\tString SETTINGS_APPEARANCE_TEXT = getStringResource(R.string.settings_cat_appearance);\n\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tfor (String name : taskListNames) {\n\t\t\tEspressoHelper.createTaskList(name);\n\t\t\tEspressoHelper.openDrawer();\n\t\t}\n\t\tEspressoHelper.openDrawer();\n\n\t\t// onData() can scroll to the item, but can't click it\n\t\tonData(CursorMatchers\n\t\t\t\t.withRowString(\"title\", equalToIgnoringCase(\"ut \")))\n\t\t\t\t.inAdapterView(withId(R.id.leftDrawer))\n\t\t\t\t.perform(scrollTo())\n\t\t\t\t.check(matches(isDisplayed()));\n\n\t\t// onView() can click on the item, but can't scroll to it\n\t\tonView(allOf(\n\t\t\t\t\twithText(equalToIgnoringCase(\"ut \")),\n\t\t\t\t\twithId(android.R.id.text1)))\n\t\t\t\t.perform(click());\n\n\t\tEspressoHelper.openDrawer();\n\n\t\t// open the preferences page and check that it is visible\n\t\topenContextualActionModeOverflowMenu();\n\t\tonView(withText(SETTINGS_TEXT)).perform(click());\n\t\tonView(withText(SETTINGS_APPEARANCE_TEXT))\n\t\t\t\t.check(matches(isDisplayed()));\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCompletedTasksAreCleared.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onData;\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;\nimport static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;\nimport static androidx.test.espresso.matcher.ViewMatchers.hasChildCount;\nimport static androidx.test.espresso.matcher.ViewMatchers.isRoot;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\nimport static org.hamcrest.Matchers.anything;\n\nimport androidx.test.espresso.DataInteraction;\nimport androidx.test.espresso.Espresso;\nimport androidx.test.espresso.PerformException;\nimport androidx.test.filters.LargeTest;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestCompletedTasksAreCleared extends BaseTestClass {\n\n\tString noteName1, noteName2, noteName3, noteName4;\n\n\t@Before\n\tpublic void initStrings() {\n\t\tnoteName1 = \"prepare food\";\n\t\tnoteName2 = \"take dogs out\";\n\t\tnoteName3 = \"water plants\";\n\t\tnoteName4 = \"sleep\";\n\t}\n\n\t@Test\n\tpublic void testCompletedTasksAreCleared() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tString[] noteNames = { noteName1, noteName2, noteName3, noteName4 };\n\t\tEspressoHelper.createNotes(noteNames);\n\n\t\tEspressoHelper.navigateUp();\n\t\tclickCheckBoxAt(1);\n\t\tclickCheckBoxAt(3);\n\n\t\t//clear notes\n\t\topenContextualActionModeOverflowMenu();\n\t\tString CLEAR_COMPLETED = getStringResource(R.string.menu_clearcompleted);\n\t\tonView(withText(CLEAR_COMPLETED)).perform(click());\n\t\tonView(withId(android.R.id.button1)).perform(click());\n\n\t\t//check that the notes do not exist any more\n\t\tonView(withText(noteNames[0]))\n\t\t\t\t.check(doesNotExist());\n\t\tonView(withText(noteNames[2]))\n\t\t\t\t.check(doesNotExist());\n\t}\n\n\t/**\n\t * this function expects the list to have 5 children (=notes): the 4 added in this test\n\t * + the default 'welcome' note\n\t */\n\tprivate void clickCheckBoxAt(int position) {\n\t\t// the keyboard may cover the notes, which makes the next lines crash!\n\t\ttry {\n\t\t\tEspresso.closeSoftKeyboard();\n\t\t} catch (PerformException ignored) {\n\t\t\t// keyboard was already closed\n\t\t}\n\t\tDataInteraction di = onData(anything())\n\t\t\t\t.inAdapterView(allOf(withId(android.R.id.list), hasChildCount(5)))\n\t\t\t\t.atPosition(position)\n\t\t\t\t.onChildView(withId(R.id.checkbox));\n\t\tdi.perform(click());\n\t}\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateNoteAndDeleteIt.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\n\nimport androidx.test.filters.LargeTest;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestCreateNoteAndDeleteIt extends BaseTestClass {\n\n\tprivate String noteName1;\n\n\t@Before\n\tpublic void initStrings() {\n\t\tnoteName1 = \"prepare food\";\n\t}\n\n\t@Test\n\tpublic void testCreateNoteAndDeleteIt() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tEspressoHelper.createNoteWithName(noteName1);\n\t\tEspressoHelper.navigateUp();\n\n\t\tonView(withText(equalToIgnoringCase(noteName1))).perform(click());\n\t\tonView(withId(R.id.menu_delete)).perform(click());\n\t\tonView(withId(android.R.id.button1)).perform(click());\n\n\t\t// assert that we're back in the list\n\t\tonView(withId(R.id.menu_search)).check(matches(isDisplayed()));\n\n\t\t//check that the view is not visible\n\t\tonView(withText(equalToIgnoringCase(noteName1))).check(doesNotExist());\n\t}\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateTaskListAndDeleteIt.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;\nimport static androidx.test.espresso.action.ViewActions.longClick;\nimport static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;\nimport static androidx.test.espresso.matcher.RootMatchers.isDialog;\nimport static androidx.test.espresso.matcher.ViewMatchers.isRoot;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\n\nimport androidx.test.filters.LargeTest;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n@LargeTest\npublic class TestCreateTaskListAndDeleteIt extends BaseTestClass {\n\n\tprivate String taskListName;\n\n\t@Before\n\tpublic void initStrings() {\n\t\ttaskListName = \"a random task list\";\n\t}\n\n\t@Test\n\tpublic void testCreateTaskListAndDeleteIt() {\n\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\t\tEspressoHelper.createTaskList(taskListName);\n\n\t\tEspressoHelper.openDrawer();\n\n\t\tonView(allOf(withText(taskListName), withId(android.R.id.text1))).perform(longClick());\n\t\tEspressoHelper.waitUi();\n\n\t\tonView(isRoot()).inRoot(isDialog()).perform(closeSoftKeyboard());\n\t\tonView(withId(R.id.deleteButton)).perform(click());\n\n\t\tonView(withText(this.getStringResource(android.R.string.ok))).perform(click());\n\t\tonView(withText(taskListName)).check(doesNotExist());\n\t}\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestPasswords.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.action.ViewActions.typeText;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\nimport static org.hamcrest.Matchers.instanceOf;\nimport static org.hamcrest.Matchers.equalToIgnoringCase;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.ui.TitleNoteTextView;\n\nimport org.junit.Test;\n\npublic class TestPasswords extends BaseTestClass {\n\n\t// when the note is locked, only this is shown\n\tfinal String noteTitle = \"this is my note\";\n\n\t// note tile + content, shown in the edittext\n\tfinal String fullNoteText1 = noteTitle + \"\\n.\\ncontent line\";\n\n\t// typed into the popup\n\tfinal String password = \"itsnotasecrettoanybody\";\n\n\t@Test\n\tpublic void testAddNoteLockWithPassword() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\tEspressoHelper.createNoteWithName(fullNoteText1);\n\t\tEspressoHelper.navigateUp();\n\n\t\tonView(withText(equalToIgnoringCase(fullNoteText1))).check(matches(isDisplayed()));\n\t\tonView(withText(equalToIgnoringCase(fullNoteText1))).perform(click());\n\t\topenContextualActionModeOverflowMenu();\n\n\t\tString MENU_TEXT = getStringResource(R.string.lock_note);\n\t\tonView(withText(MENU_TEXT)).perform(click());\n\n\t\tEspressoHelper\n\t\t\t\t.onViewWithIdInDialog(R.id.passwordField)\n\t\t\t\t.perform(typeText(password));\n\t\tEspressoHelper\n\t\t\t\t.onViewWithIdInDialog(R.id.passwordVerificationField)\n\t\t\t\t.perform(typeText(password));\n\t\tEspressoHelper\n\t\t\t\t.onViewWithIdInDialog(R.id.dialog_yes)\n\t\t\t\t.perform(click());\n\t\tEspressoHelper.waitUi();\n\n\t\t// then it opens the popup again, to ask the password\n\t\tEspressoHelper\n\t\t\t\t.onViewWithIdInDialog(R.id.passwordField)\n\t\t\t\t.check(matches(isDisplayed()));\n\t\tEspressoHelper\n\t\t\t\t.onViewWithIdInDialog(R.id.passwordField)\n\t\t\t\t.perform(typeText(password));\n\t\tEspressoHelper\n\t\t\t\t.onViewWithIdInDialog(R.id.dialog_yes)\n\t\t\t\t.check(matches(isDisplayed()));\n\t\tEspressoHelper\n\t\t\t\t.onViewWithIdInDialog(R.id.dialog_yes)\n\t\t\t\t.perform(click());\n\n\t\t// the note on the (custom) edittext should appear correctly\n\t\tonView(withId(R.id.taskText))\n\t\t\t\t.check(matches(withText(equalToIgnoringCase(fullNoteText1))));\n\t\tEspressoHelper.navigateUp();\n\n\t\t// in the list view, only the title is shown\n\t\tonView(allOf(\n\t\t\t\t\twithText(equalToIgnoringCase(noteTitle)),\n\t\t\t\t\tinstanceOf(TitleNoteTextView.class)))\n\t\t\t\t.check(matches(isDisplayed()));\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestSaveLoadJsonBackup.java",
    "content": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onData;\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;\nimport static androidx.test.espresso.action.ViewActions.click;\nimport static androidx.test.espresso.action.ViewActions.scrollTo;\nimport static androidx.test.espresso.assertion.ViewAssertions.matches;\nimport static androidx.test.espresso.intent.Intents.intending;\nimport static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;\nimport static androidx.test.espresso.matcher.ViewMatchers.hasChildCount;\nimport static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static androidx.test.espresso.matcher.ViewMatchers.withId;\nimport static androidx.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.Matchers.allOf;\nimport static org.hamcrest.Matchers.anything;\n\nimport android.app.Activity;\nimport android.app.Instrumentation;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\n\nimport androidx.documentfile.provider.DocumentFile;\nimport androidx.test.filters.LargeTest;\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.junit.Test;\n\n@LargeTest\npublic class TestSaveLoadJsonBackup extends BaseTestClass {\n\n\tfinal String noteText1 = \"random note\";\n\tfinal String noteText2 = \"other random note\";\n\n\t/**\n\t * @return an intent that replicates the response of the system's filepicker,\n\t * using a custom fileprovider\n\t */\n\tprivate static Instrumentation.ActivityResult createResponseIntent() {\n\t\tContext context = InstrumentationRegistry\n\t\t\t\t.getInstrumentation()\n\t\t\t\t.getTargetContext();\n\n\t\t// TODO here you have to create a uri for a documentfile that represents a\n\t\t//  folder where we can write files. good luck! There's no explanation of this anywhere\n\n\t\tUri uri1 = Uri.parse(\"content://com.android.externalstorage.documents/tree/primary%3Agoogletest%2Ftest_outputfiles\");\n\t\tvar docDir = DocumentFile.fromTreeUri(context, uri1);\n\t\tvar uri2 = docDir.getUri();\n\n\t\tIntent i = new Intent()\n\t\t\t\t.setData(uri2)\n\t\t\t\t.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n\t\t\t\t.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n\t\treturn new Instrumentation.ActivityResult(Activity.RESULT_OK, i);\n\t}\n\n\t@Test\n\tpublic void testSaveLoadBackup() {\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\n\t\t// add 2 notes\n\t\tEspressoHelper.createNoteWithName(noteText1);\n\t\tEspressoHelper.createNoteWithName(noteText2);\n\n\t\tEspressoHelper.navigateUp();\n\n\t\t// make a backup\n\t\topenContextualActionModeOverflowMenu();\n\t\tString SETTINGS_TEXT = getStringResource(R.string.menu_preferences);\n\t\tonView(withText(SETTINGS_TEXT))\n\t\t\t\t.perform(scrollTo()) // in case the keyboard is covering the menu\n\t\t\t\t.perform(click());\n\n\t\tString SETTINGS_BACKUP_TEXT = getStringResource(R.string.backup);\n\t\tonView(withText(SETTINGS_BACKUP_TEXT)).perform(click());\n\n\t\t// register answer to mock the filepicker\n\t\tintending(hasAction(Intent.ACTION_OPEN_DOCUMENT_TREE))\n\t\t\t\t.respondWith(createResponseIntent());\n\n\t\t// choose a backup directory\n\t\tString CHOOSE_DIR = getStringResource(R.string.choose_backup_folder);\n\t\tonView(withText(CHOOSE_DIR)).perform();\n\n\t\t// then click export\n\t\tString EXPORT_BACKUP_TEXT = getStringResource(R.string.backup_export);\n\t\tonView(withText(EXPORT_BACKUP_TEXT)).perform(click());\n\t\tEspressoHelper.onViewWithIdInDialog(android.R.id.button1).check(matches(isDisplayed()));\n\t\tEspressoHelper.onViewWithIdInDialog(android.R.id.button1).perform(click());\n\t\tEspressoHelper.waitUi();\n\n\t\t// return to the notes list\n\t\tEspressoHelper.exitPrefsActivity();\n\n\t\t// from here on, it does not work. check createResponseIntent()\n\t\tif (true) return;\n\n\t\t// check & delete both notes\n\t\tclickCheckBoxAt(0);\n\t\tclickCheckBoxAt(1);\n\n\t\t// clear completed notes\n\t\topenContextualActionModeOverflowMenu();\n\t\tString CLEAR_COMPLETED = getStringResource(R.string.menu_clearcompleted);\n\t\tonView(withText(CLEAR_COMPLETED)).perform(click());\n\t\tEspressoHelper.onViewWithIdInDialog(android.R.id.button1).check(matches(isDisplayed()));\n\t\tEspressoHelper.onViewWithIdInDialog(android.R.id.button1).perform(click());\n\n\t\t// restore the backup\n\t\topenContextualActionModeOverflowMenu();\n\t\tonView(withText(SETTINGS_TEXT)).perform(click());\n\t\tonView(withText(SETTINGS_BACKUP_TEXT)).perform(click());\n\n\t\tString IMPORT_BACKUP_TEXT = getStringResource(R.string.backup_import);\n\t\tonView(withText(IMPORT_BACKUP_TEXT)).perform(click());\n\t\tEspressoHelper.onViewWithIdInDialog(android.R.id.button1).perform(click());\n\t\tEspressoHelper.waitUi();\n\n\t\t// return to the notes list\n\t\tEspressoHelper.exitPrefsActivity();\n\n\t\t// ensure both notes were restored\n\t\tonView(withText(noteText1)).check(matches(isDisplayed()));\n\t\tonView(withText(noteText2)).check(matches(isDisplayed()));\n\t}\n\n\t// TODO explore accessibility tests. See\n\t//  https://developer.android.com/training/testing/espresso/accessibility-checking\n\n\t// this one expects the list to have 2 children\n\tprivate void clickCheckBoxAt(int position) {\n\t\tvar i = onData(anything())\n\t\t\t\t.inAdapterView(allOf(withId(android.R.id.list), hasChildCount(2)))\n\t\t\t\t.atPosition(position)\n\t\t\t\t.onChildView(withId(R.id.checkbox));\n\t\ti.perform(click());\n\t}\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBFreshTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\n\nimport androidx.test.filters.SmallTest;\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.notepad.database.DatabaseHandler;\nimport com.nononsenseapps.notepad.database.LegacyDBHelper;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport junit.framework.TestCase;\n\npublic class DBFreshTest extends TestCase {\n\n\tstatic final String PREFIX = \"fresh_test_\";\n\n\tprivate Context context;\n\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tcontext = InstrumentationRegistry.getInstrumentation().getTargetContext();\n\t}\n\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t}\n\n\t@SmallTest\n\tpublic void testFreshInstall() {\n\t\tcontext.deleteDatabase(PREFIX + LegacyDBHelper.LEGACY_DATABASE_NAME);\n\t\tcontext.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME);\n\t\tfinal SQLiteDatabase db = new DatabaseHandler(context, PREFIX).getReadableDatabase();\n\t\t// Just open the database, there should be one list and one task present\n\t\tCursor tlc = db.query(TaskList.TABLE_NAME, TaskList.Columns.FIELDS,\n\t\t\t\tnull, null, null, null, null);\n\n\t\tassertEquals(\"Should be ONE list present on fresh installs\",\n\t\t\t\t1, tlc.getCount());\n\t\ttlc.close();\n\n\t\tCursor tc = db.query(Task.TABLE_NAME, Task.Columns.FIELDS,\n\t\t\t\tnull, null, null, null, null);\n\t\tassertEquals(\"Should be 1 task present on fresh installs, the 'welcome' task\",\n\t\t\t\t1, tc.getCount());\n\t\ttc.close();\n\n\t\tdb.close();\n\t\tassertTrue(\"Could not delete database\",\n\t\t\t\tcontext.deleteDatabase(PREFIX + LegacyDBHelper.LEGACY_DATABASE_NAME));\n\t\tassertTrue(\"Could not delete database\",\n\t\t\t\tcontext.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME));\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderMovementTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport android.content.ContentResolver;\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteConstraintException;\nimport android.net.Uri;\nimport android.util.Log;\n\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.notepad.database.DAO;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport junit.framework.TestCase;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Random;\n\npublic class DBProviderMovementTest extends TestCase {\n\n\tprivate ContentResolver resolver;\n\n\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tresolver = InstrumentationRegistry\n\t\t\t\t.getInstrumentation()\n\t\t\t\t.getTargetContext()\n\t\t\t\t.getContentResolver();\n\t}\n\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t}\n\n\t/*\n\t * Util methods\n\t */\n\n\tprivate void assertUriReturnsResult(final Uri uri, final String[] fields) {\n\t\tassertUriReturnsResult(uri, fields, null, null);\n\t}\n\n\tprivate void assertUriReturnsResult(final Uri uri, final String[] fields,\n\t\t\t\t\t\t\t\t\t\tfinal String where, final String[] whereArgs) {\n\t\tfinal Cursor c = resolver.query(uri, fields, where, whereArgs, null);\n\t\tfinal boolean notEmpty = c.moveToFirst();\n\t\tc.close();\n\t\tassertTrue(\"Uri did not return a result: \" + uri.getEncodedPath(),\n\t\t\t\tnotEmpty);\n\t}\n\n\tprivate Cursor assertCursorGood(final Cursor c) {\n\t\tassertNotNull(c);\n\t\tassertFalse(c.isClosed());\n\n\t\treturn c;\n\t}\n\n\tprivate void assertTasksCountIs(final long listId, final int count) {\n\t\tassertEquals(count, getTasks(listId).size());\n\t}\n\n\tprivate void assertTaskLeftRightAreSequential(final long listId) {\n\t\t// Get ordered\n\t\tArrayList<Task> tasks = getTasks(listId);\n\t\tlong prev = 0;\n\t\tfor (Task t : tasks) {\n\t\t\tassertTrue(\"Left must be less than right! \" + t.left + \" !< \"\n\t\t\t\t\t+ t.right, t.left < t.right);\n\t\t\tassertTrue(\"Previous item must have smaller left\",\n\t\t\t\t\tprev < t.left);\n\t\t\tif (t.right == t.left + 1) {\n\t\t\t\tprev = t.right;\n\t\t\t} else {\n\t\t\t\tprev = t.left;\n\t\t\t}\n\t\t}\n\n\t\t// Test maximum value\n\t\tCursor c = resolver.query(Task.URI, Task.Columns.FIELDS,\n\t\t\t\tTask.Columns.DBLIST + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(listId) }, Task.Columns.RIGHT + \" DESC\");\n\t\tassertCursorGood(c);\n\t\tHashSet<Long> positions = new HashSet<>();\n\t\tif (c.getCount() > 0) {\n\t\t\t// Right most will be twice the number of tasks\n\t\t\tassertTrue(c.moveToFirst());\n\t\t\tfinal Task last = new Task(c);\n\n\t\t\tassertEquals(String.format(Locale.US, \"%d != 2 * %d\", last.right, c.getCount()),\n\t\t\t\t\t(long) last.right, 2L * c.getCount());\n\n\t\t\t// Make sure there are no duplicates and such\n\t\t\tfor (long i = 1; i <= c.getCount() * 2L; i++) {\n\t\t\t\tpositions.add(i);\n\t\t\t}\n\n\t\t\tpositions.remove(last.left);\n\t\t\tpositions.remove(last.right);\n\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tTask task = new Task(c);\n\t\t\t\tpositions.remove(task.left);\n\t\t\t\tpositions.remove(task.right);\n\t\t\t}\n\t\t}\n\t\tc.close();\n\n\t\tassertEquals(\"Must be duplicate positions in the list\", 0,\n\t\t\t\tpositions.size());\n\t}\n\n\tprivate TaskList insertList() {\n\t\tfinal TaskList tl = new TaskList();\n\t\ttl.title = \"A test list\";\n\n\t\ttl.setId(resolver.insert(tl.getBaseUri(), tl.getContent()));\n\n\t\tassertTrue(0 < tl._id);\n\n\t\treturn tl;\n\t}\n\n\tprivate void deleteList(final TaskList tl) {\n\t\tassertTrue(0 < resolver.delete(tl.getUri(), null, null));\n\t}\n\n\tprivate ArrayList<Task> insertTasks(final long listId, final int number) {\n\t\tArrayList<Task> results = new ArrayList<>(number);\n\t\tfor (int i = 0; i < number; i++) {\n\t\t\tint count = 0;\n\t\t\tTask t = new Task();\n\t\t\tt.title = \"Task\" + ++count;\n\t\t\tt.dblist = listId;\n\t\t\tUri uri = resolver.insert(Task.URI, t.getContent());\n\t\t\tif (uri != null) {\n\t\t\t\tt.setId(uri);\n\t\t\t\tresults.add(t);\n\t\t\t}\n\t\t\tassertTaskLeftRightAreSequential(listId);\n\t\t}\n\t\tassertTaskLeftRightAreSequential(listId);\n\t\tassertTasksCountIs(listId, number);\n\n\t\treturn results;\n\t}\n\n\tprivate ArrayList<Task> getTasks(final long listId) {\n\t\tfinal ArrayList<Task> results = new ArrayList<>();\n\t\tfinal Cursor c = resolver.query(Task.URI,\n\t\t\t\tTask.Columns.FIELDS, Task.Columns.DBLIST + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(listId) }, Task.Columns.LEFT);\n\t\tassertCursorGood(c);\n\t\twhile (c.moveToNext()) {\n\t\t\tresults.add(new Task(c));\n\t\t}\n\t\tc.close();\n\n\t\treturn results;\n\t}\n\n\tprivate Task getTask(final long id) {\n\t\tfinal Cursor c = resolver.query(Task.getUri(id), Task.Columns.FIELDS,\n\t\t\t\tnull, null, null);\n\t\tassertCursorGood(c);\n\t\tTask t = null;\n\t\tif (c.moveToFirst()) {\n\t\t\tt = new Task(c);\n\t\t}\n\t\tc.close();\n\n\t\treturn t;\n\t}\n\n\tprivate ArrayList<Task> getDeletedTask(final String title) {\n\t\tfinal ArrayList<Task> results = new ArrayList<>();\n\t\tfinal Cursor c = resolver.query(Task.URI_DELETED_QUERY,\n\t\t\t\tTask.Columns.FIELDS, Task.Columns.TITLE + \"IS ?\",\n\t\t\t\tnew String[] { title }, null);\n\t\tassertCursorGood(c);\n\t\twhile (c.moveToNext()) {\n\t\t\tresults.add(new Task(c));\n\t\t}\n\t\tc.close();\n\n\t\treturn results;\n\t}\n\n\tprivate void deleteTasks(final ArrayList<Task> tasks) {\n\t\tfor (final Task t : tasks) {\n\t\t\tdeleteTask(t);\n\t\t}\n\t}\n\n\tprivate void deleteTask(Task t) {\n\t\tassertTrue(0 < resolver.delete(t.getUri(), null, null));\n\t\tassertTaskLeftRightAreSequential(t.dblist);\n\t}\n\n\tprivate void moveTasksToList(final TaskList tl, final Task... ts) {\n\t\tlong[] ids = new long[ts.length];\n\t\tfor (int i = 0; i < ts.length; i++) {\n\t\t\tids[i] = ts[i]._id;\n\t\t}\n\n\t\tfinal ContentValues val = new ContentValues();\n\t\tval.put(Task.Columns.DBLIST, tl._id);\n\n\t\t// where _ID in (1, 2, 3)\n\t\tfinal String whereId = Task.Columns._ID + \" IN (\" + DAO.arrayToCommaString(ids) + \")\";\n\n\t\tvar mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();\n\t\tmContext.getContentResolver().update(Task.URI, val, whereId, null);\n\n\t\t// Verify that task was moved\n\t\t// Check new\n\t\tassertTaskLeftRightAreSequential(tl._id);\n\t}\n\n\tprivate ArrayList<Task> moveAndAssert(final TaskList tl, final int fromPos,\n\t\t\t\t\t\t\t\t\t\t  final int toPos) {\n\t\tLog.i(\"nononsenseapps test\", \"Testing move from: \" + fromPos + \" to \"\n\t\t\t\t+ toPos);\n\t\t// Get ordered\n\t\tfinal ArrayList<Task> oldtasks = getTasks(tl._id);\n\n\t\t// Move 5 to 4\n\t\tfinal Task movingTask = oldtasks.get(fromPos);\n\t\tfinal Task targetTask = oldtasks.get(toPos);\n\n\t\tfinal int result = movingTask.moveTo(resolver, targetTask);\n\n\t\t// Verity that things changed or not\n\t\tif (movingTask._id != targetTask._id)\n\t\t\tassertTrue(\"Moving a task should update rows\", 0 < result);\n\t\telse\n\t\t\tassertEquals(\"Moving a task to itself shouldn't change anything\", 0, result);\n\n\t\t// Find new values\n\t\tfinal ArrayList<Task> newtasks = getTasks(tl._id);\n\t\tTask newone = null;\n\t\tTask newtarget = null;\n\n\t\tfor (Task t : newtasks) {\n\t\t\tif (t._id == movingTask._id) {\n\t\t\t\tnewone = t;\n\t\t\t}\n\t\t\tif (t._id == targetTask._id) {\n\t\t\t\tnewtarget = t;\n\t\t\t}\n\t\t}\n\t\tLog.d(\"nononsenseapps test\", \"old, target, new, newtarget: \"\n\t\t\t\t+ movingTask.left + \",\" + movingTask.right + \" \"\n\t\t\t\t+ targetTask.left + \",\" + targetTask.right + \" \" + newone.left\n\t\t\t\t+ \",\" + newone.right + \" \" + newtarget.left + \",\"\n\t\t\t\t+ newtarget.right);\n\n\t\tassertNotNull(\"Couldnt find the moved task\", newone);\n\n\t\tif (targetTask.left < movingTask.left) {\n\t\t\tassertEquals(\"Left value does not equal target\", targetTask.left,\n\t\t\t\t\tnewone.left);\n\t\t\tassertEquals(\"Right value does not equal target left + 1\",\n\t\t\t\t\ttargetTask.left + 1, (long) newone.right);\n\t\t} else if (targetTask.right > movingTask.right) {\n\t\t\tassertEquals(\"Left value does not equal target right - 1\",\n\t\t\t\t\ttargetTask.right - 1, (long) newone.left);\n\t\t\tassertEquals(\"Right value does not equal target\", targetTask.right,\n\t\t\t\t\tnewone.right);\n\t\t}\n\n\t\tassertEquals(\"Width should be 1 after a move\", 1, newone.right\n\t\t\t\t- newone.left);\n\n\t\t// assertEquals(\"Target should have moved 2 steps\", 2,);\n\n\t\tassertEquals(\"Number of tasks should not change\", oldtasks.size(), newtasks.size());\n\t\tassertTaskLeftRightAreSequential(tl._id);\n\n\t\treturn getTasks(tl._id);\n\t}\n\n\t/*\n\t * Test methods\n\t */\n\n\tpublic void testDeleteList() {\n\t\tfinal TaskList tl = insertList();\n\t\tfinal int count = 10;\n\t\tfinal long listId = tl._id;\n\n\t\tassertTasksCountIs(listId, 0);\n\n\t\tinsertTasks(listId, count);\n\t\tassertTasksCountIs(listId, count);\n\n\t\tdeleteList(tl);\n\t\t// Should return nothing\n\t\t// Cursor c = resolver.query(TaskList.URI, TaskList.Columns.FIELDS,\n\t\t// TaskList.Columns._ID + \" IS ?\",\n\t\t// new String[] { Long.toString(listId) }, null);\n\t\t// assertCursorGood(c);\n\n\t\t// assertEquals(\"List should be gone\", 0, c.getCount());\n\t\t// removing list should delete all tasks within\n\t\tassertTasksCountIs(listId, 0);\n\t\t// c.close();\n\t}\n\n\tpublic void testInsertAndRemoveTasks() {\n\t\tfinal TaskList tl = insertList();\n\t\tArrayList<Task> tasks = insertTasks(tl._id, 10);\n\t\tassertTasksCountIs(tl._id, 10);\n\t\tdeleteTasks(tasks);\n\t\tassertTasksCountIs(tl._id, 0);\n\t\tdeleteList(tl);\n\t}\n\n\tpublic void testInsertTaskInWrongList() {\n\t\t// Should not be possible to insert\n\t\t// into a non-existing list because of foreign key constraints\n\n\t\tfinal long wrongId = 92525;\n\t\tassertTasksCountIs(wrongId, 0);\n\n\t\tTask t = new Task();\n\t\tt.title = \"Task\";\n\t\tt.dblist = wrongId;\n\n\t\tboolean thrown = false;\n\t\tUri uri = null;\n\t\ttry {\n\t\t\turi = resolver.insert(Task.URI, t.getContent());\n\t\t} catch (SQLException e) {\n\t\t\tthrown = true;\n\t\t}\n\n\t\tassertNull(uri);\n\t\tassertTasksCountIs(wrongId, 0);\n\t}\n\n\tpublic void testInvalidPos() {\n\t\t// Positions must be greater than 0\n\t\t// or should throw constraint failed\n\n\t\tTaskList tl = insertList();\n\t\tArrayList<Task> ts = insertTasks(tl._id, 1);\n\n\t\tTask t = ts.get(0);\n\n\t\tt.left = 0L;\n\n\t\tboolean failed = false;\n\t\ttry {\n\t\t\tresolver.update(t.getUri(), t.getContent(), null, null);\n\t\t} catch (SQLiteConstraintException e) {\n\t\t\tfailed = true;\n\t\t}\n\n\t\t//assertTrue(\"Setting left to 0 should throw exception!\", failed);\n\n\t\tt.left = 5L;\n\t\tt.right = 0L;\n\t\tfailed = false;\n\t\ttry {\n\t\t\tresolver.update(t.getUri(), t.getContent(), null, null);\n\t\t} catch (SQLiteConstraintException e) {\n\t\t\tfailed = true;\n\t\t}\n\t\t//assertTrue(\"Setting right to 0 should throw exception\", failed);\n\n\t\tdeleteList(tl);\n\t}\n\n\tpublic void testMoveTask() {\n\t\tfinal TaskList tl = insertList();\n\t\tint count = 10;\n\t\tinsertTasks(tl._id, count);\n\t\tassertTaskLeftRightAreSequential(tl._id);\n\n\t\t// Move some tasks around\n\t\tmoveAndAssert(tl, 0, count - 1);\n\t\tmoveAndAssert(tl, count - 1, 0);\n\n\t\tmoveAndAssert(tl, 1, count - 2);\n\t\tmoveAndAssert(tl, count - 2, 1);\n\n\t\tmoveAndAssert(tl, 4, 0);\n\t\tmoveAndAssert(tl, 4, 9);\n\n\t\tfor (int i = 0; i < count * 2; i++) {\n\t\t\tmoveAndAssert(tl, 0, count - 1);\n\t\t}\n\t\tfor (int i = 0; i < count * 2; i++) {\n\t\t\tmoveAndAssert(tl, count - 2, 2);\n\t\t}\n\n\t\tRandom rand = new Random();\n\t\tint min = 0, max = count - 1;\n\t\tfor (int i = 0; i < count * 2; i++) {\n\t\t\tint fromPos = rand.nextInt(max - min + 1) + min;\n\t\t\tint toPos = fromPos;\n\t\t\twhile (toPos == fromPos) {\n\t\t\t\ttoPos = rand.nextInt(max - min + 1) + min;\n\t\t\t}\n\t\t\t// two unique positions generated, now move\n\t\t\tmoveAndAssert(tl, fromPos, toPos);\n\t\t}\n\n\t\t// Clean up\n\t\tdeleteList(tl);\n\t}\n\n\tpublic void testMoveTaskToList() {\n\t\tfinal TaskList tl = insertList();\n\t\tfinal TaskList tl2 = insertList();\n\t\tint count = 10;\n\t\tArrayList<Task> tasks1 = insertTasks(tl._id, count);\n\t\tArrayList<Task> tasks2 = insertTasks(tl2._id, count);\n\t\tassertTaskLeftRightAreSequential(tl._id);\n\t\tassertTaskLeftRightAreSequential(tl2._id);\n\n\t\t// Move some tasks around\n\n\n\t\tRandom rand = new Random();\n\t\tint min = 0, max = count - 1;\n\t\tfor (int i = 0; i < 100; i++) {\n\t\t\tif (rand.nextBoolean() && tasks1.size() > 1) {\n\t\t\t\tint taskIndex = rand.nextInt(tasks1.size());\n\t\t\t\tTask t1 = tasks1.remove(taskIndex);\n\t\t\t\ttaskIndex = rand.nextInt(tasks1.size());\n\t\t\t\tTask t2 = tasks1.remove(taskIndex);\n\t\t\t\tmoveTasksToList(tl2, t1, t2);\n\t\t\t\ttasks2.add(t1);\n\t\t\t\ttasks2.add(t2);\n\t\t\t} else if (tasks2.size() > 1) {\n\t\t\t\tint taskIndex = rand.nextInt(tasks2.size());\n\t\t\t\tTask t1 = tasks2.remove(taskIndex);\n\t\t\t\ttaskIndex = rand.nextInt(tasks2.size());\n\t\t\t\tTask t2 = tasks2.remove(taskIndex);\n\t\t\t\tmoveTasksToList(tl, t1, t2);\n\t\t\t\ttasks1.add(t1);\n\t\t\t\ttasks1.add(t2);\n\t\t\t}\n\t\t}\n\n\t\t// Clean up\n\t\tdeleteList(tl);\n\t\tdeleteList(tl2);\n\t}\n\n//\tpublic void testIndents() {\n//\t\tfinal TaskList tl = insertList();\n//\t\tint count = 7;\n//\t\tinsertTasks(tl._id, count);\n//\t\tArrayList<Task> orgTasks = getTasks(tl._id);\n//\n//\t\t// Indenting the first item should fail (not change anything)\n//\t\t// as it's impossible to do\n//\n//\t\tindentAndAssert(orgTasks.get(0), false);\n//\n//\t\t// Test a successful one\n//\t\t/*\n//\t\t * a0 b1 c0 d0 e0\n//\t\t */\n//\t\torgTasks = indentAndAssert(orgTasks.get(1), true);\n//\t\tassertEquals(\"Task level should be two\", 2, orgTasks.get(1).level);\n//\n//\t\t// Indenting it again should fail though\n//\t\t/*\n//\t\t * a0 b1 c0 d0 e0 f0 g0\n//\t\t */\n//\t\torgTasks = indentAndAssert(orgTasks.get(1), false);\n//\t\tassertEquals(\"Task level should still be two\", 2, orgTasks.get(1).level);\n//\n//\t\t// Try last item\n//\t\t/*\n//\t\t * a0 b1 c0 d0 e0 f0 g1\n//\t\t */\n//\t\torgTasks = indentAndAssert(orgTasks.get(count - 1), true);\n//\t\tassertEquals(\"Task level should be two\", 2,\n//\t\t\t\torgTasks.get(count - 1).level);\n//\n//\t\t/*\n//\t\t * a0 b1 c2 d1 e0 f1 g2\n//\t\t */\n//\t\torgTasks = indentAndAssert(orgTasks.get(2), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(2), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(2), false);\n//\t\tassertEquals(\"Task level should be three\", 3, orgTasks.get(2).level);\n//\t\torgTasks = indentAndAssert(orgTasks.get(3), true);\n//\t\tassertEquals(\"Task level incorrect\", 2, orgTasks.get(3).level);\n//\t\torgTasks = indentAndAssert(orgTasks.get(5), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(5), false);\n//\t\tassertEquals(\"Task level incorrect\", 2, orgTasks.get(5).level);\n//\t\torgTasks = indentAndAssert(orgTasks.get(6), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(6), false);\n//\t\tassertEquals(\"Task level incorrect\", 3, orgTasks.get(6).level);\n//\n//\t\t/*\n//\t\t * a0 b1 c2 d3 e4 f5 g6\n//\t\t */\n//\t\torgTasks = indentAndAssert(orgTasks.get(0), false);\n//\t\torgTasks = indentAndAssert(orgTasks.get(1), false);\n//\t\torgTasks = indentAndAssert(orgTasks.get(2), false);\n//\t\torgTasks = indentAndAssert(orgTasks.get(3), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(3), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(3), false);\n//\t\torgTasks = indentAndAssert(orgTasks.get(4), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(4), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(4), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(4), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(4), false);\n//\t\torgTasks = indentAndAssert(orgTasks.get(5), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(5), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(5), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(5), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(5), false);\n//\t\torgTasks = indentAndAssert(orgTasks.get(6), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(6), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(6), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(6), true);\n//\t\torgTasks = indentAndAssert(orgTasks.get(6), false);\n//\n//\t\tfor (int i = 0; i < orgTasks.size(); i++) {\n//\t\t\tassertEquals(\"Task level incorrect\", 1 + i, orgTasks.get(i).level);\n//\t\t}\n//\n//\t\t// Let's start unindenting stuff!\n//\n//\t\t// Unindent root should fail\n//\t\torgTasks = unIndentAndAssert(orgTasks.get(0), false);\n//\t\t// Last one should succeed many times\n//\t\tfor (int i = orgTasks.size(); i > 1; i--) {\n//\t\t\tassertEquals(\"Level incorrect (i = \" + i + \")\", i,\n//\t\t\t\t\torgTasks.get(orgTasks.size() - 1).level);\n//\t\t\torgTasks = unIndentAndAssert(orgTasks.get(orgTasks.size() - 1),\n//\t\t\t\t\ttrue);\n//\t\t}\n//\t\t// Should now be a root\n//\t\tassertEquals(\"Level incorrect\", 1,\n//\t\t\t\torgTasks.get(orgTasks.size() - 1).level);\n//\n//\t\t// Let's do the rest, top to bottom\n//\t\t// All preceeding items are affected by this amount\n//\t\tint cum = 0;\n//\t\tfor (int j = 1; j < orgTasks.size() - 1; j++) {\n//\t\t\tfor (int i = j + 1; i - cum > 1; i--) {\n//\t\t\t\tassertEquals(\"Level incorrect (i = \" + i + \")\", i - cum,\n//\t\t\t\t\t\torgTasks.get(j).level);\n//\t\t\t\torgTasks = unIndentAndAssert(orgTasks.get(j), true);\n//\t\t\t}\n//\t\t\tcum += 1;\n//\t\t\t// Should now be a root\n//\t\t\tassertEquals(\"Level incorrect\", 1, orgTasks.get(j).level);\n//\t\t}\n//\n//\t\tdeleteList(tl);\n//\t}\n//\n//\tpublic void testIndentsHarder() {\n//\t\t// Was something I actually did and noticed a crash\n//\t\tfinal TaskList tl = insertList();\n//\t\tint count = 3;\n//\t\tinsertTasks(tl._id, count);\n//\t\tArrayList<Task> orgTasks = getTasks(tl._id);\n//\n//\t\t// The issue is the order things are moved in the statement\n//\t\t// Moved in order of their IDs!\n//\n//\t\t// Now at 2,1,0\n//\t\t// Want 0,2,1 (where 2 is indented)\n//\t\torgTasks = moveAndAssert(tl, 2, 0);\n//\t\torgTasks = indentAndAssert(orgTasks.get(1), true);\n//\n//\t\t// This crashed by trying to set right to null\n//\t\t// Since the parent was re-assigned before the child\n//\t\torgTasks = unIndentAndAssert(orgTasks.get(1), true);\n//\n//\t\tdeleteList(tl);\n//\t}\n//\n//\tpublic void testMoveIndentedTrees() {\n//\t\t// important to test moving tasks in a tree structure\n//\t\tfinal TaskList tl = insertList();\n//\t\tint count = 9;\n//\t\tinsertTasks(tl._id, count);\n//\t\tArrayList<Task> orgTasks = getTasks(tl._id);\n//\n//\t\t// Roots at 0, 2 and 6\n//\t\t// 0\n//\t\tindentAndAssert(orgTasks.get(1), true);\n//\t\t// 2\n//\t\tindentAndAssert(orgTasks.get(3), true);\n//\t\tindentAndAssert(orgTasks.get(4), true);\n//\t\tindentAndAssert(orgTasks.get(4), true);\n//\t\tindentAndAssert(orgTasks.get(5), true);\n//\t\tindentAndAssert(orgTasks.get(5), true);\n//\t\tindentAndAssert(orgTasks.get(5), true);\n//\t\t// 6\n//\t\tindentAndAssert(orgTasks.get(7), true);\n//\t\tindentAndAssert(orgTasks.get(8), true);\n//\t\tindentAndAssert(orgTasks.get(8), true);\n//\n//\t\t// move 2 tree to bottom\n//\t\t// now part of six tree\n//\t\tmoveAndAssert(tl, 2, 8);\n//\n//\t\t// Move 6 tree to top\n//\t\tmoveAndAssert(tl, 3, 0);\n//\n//\t\t// Move 2 tree to top\n//\t\tmoveAndAssert(tl, 6, 0);\n//\n//\t\tdeleteList(tl);\n//\t}\n\n\n\tpublic void testTaskContent() {\n\t\tTask t = new Task();\n\t\tt.title = \"Hej\";\n\t\tt.dblist = 1L;\n\t\tContentValues values = t.getContent();\n\n\t\tassertFalse(values.containsKey(Task.Columns.LEFT));\n\t\tassertFalse(values.containsKey(Task.Columns.RIGHT));\n\t}\n\n\tpublic void testDeleteTrigger() {\n\t\t// Deleting an item should place a copy of it in the delete-table\n\t\tfinal TaskList tl = insertList();\n\t\tinsertTasks(tl._id, 1);\n\t\tArrayList<Task> orgTasks = getTasks(tl._id);\n\n\t\tfinal Task orgTask = orgTasks.get(0);\n\n\t\tfinal String t = \"HABANA MAMAMANA\";\n\t\torgTask.title = t;\n\n\t\tresolver.update(orgTask.getUri(), orgTask.getContent(), null, null);\n\t\tresolver.delete(orgTask.getUri(), null, null);\n\n\t\torgTasks = getTasks(tl._id);\n\n\t\tassertEquals(\"List should be empty now\", 0, orgTasks.size());\n\n\t\t// Get the item from backup table instead\n\t\tCursor c = resolver.query(Task.URI_DELETED_QUERY,\n\t\t\t\tTask.Columns.DELETEFIELDS, Task.Columns.TITLE + \" IS ?\",\n\t\t\t\tnew String[] { t }, null);\n\n\t\tassertTrue(\"Task should be found in delete table after delete!\",\n\t\t\t\tc.moveToFirst());\n\n\t\tdeleteList(tl);\n\n\t\tc.close();\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.net.Uri;\nimport android.os.SystemClock;\n\nimport androidx.preference.PreferenceManager;\nimport androidx.test.filters.MediumTest;\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.notepad.database.DatabaseHandler;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport junit.framework.TestCase;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.List;\n\npublic class DBProviderTest extends TestCase {\n\n\tprivate Context mContext;\n\tprivate ContentResolver mResolver;\n\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tmContext = InstrumentationRegistry.getInstrumentation().getTargetContext();\n\t\tmResolver = mContext.getContentResolver();\n\n\t\t// clear app data\n\t\tPreferenceManager.getDefaultSharedPreferences(mContext).edit().clear().commit();\n\t\tDatabaseHandler.resetDatabase(mContext);\n\t}\n\n\tprivate void assertUriReturnsResult(final Uri uri, final String[] fields) {\n\t\tassertUriReturnsResult(uri, fields, null, null, -1);\n\t}\n\n\tprivate void assertUriReturnsResult(final Uri uri, final String[] fields, final String where,\n\t\t\t\t\t\t\t\t\t\tfinal String[] whereArgs, final int count) {\n\t\tfinal Cursor c = mResolver\n\t\t\t\t.query(uri, fields, where, whereArgs, null);\n\t\tassertNotNull(c);\n\n\t\tString READABLE_CURSOR_DUMP = DatabaseUtils.dumpCursorToString(c);\n\t\tNnnLogger.debug(DBProviderTest.class, READABLE_CURSOR_DUMP);\n\n\t\tfinal int cursorCount = c.getCount();\n\t\tc.close();\n\t\tif (count < 0) {\n\t\t\tassertTrue(\"Uri did not return a result: \" + uri.getEncodedPath(),\n\t\t\t\t\tcursorCount > 0);\n\t\t} else {\n\t\t\t// I don't know, sometimes it happens...\n\t\t\tassertEquals(\"Uri did not return expected number of results!\",\n\t\t\t\t\tcount, cursorCount);\n\t\t}\n\t}\n\n\tprivate TaskList getNewList() {\n\t\tTaskList result = new TaskList();\n\t\tresult.title = \"111aaTestingList\";\n\t\tresult.save(mContext);\n\t\treturn result;\n\t}\n\n\tprivate ArrayList<Task> insertSomeTasks(final TaskList list, final int count) {\n\t\tArrayList<Task> tasks = new ArrayList<>();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tTask t = new Task();\n\t\t\tt.title = \"testTask\" + i;\n\t\t\tt.note = \"testNote\" + i;\n\t\t\tt.due = Calendar.getInstance().getTimeInMillis();\n\t\t\tt.dblist = list._id;\n\t\t\tt.save(mContext);\n\t\t\ttasks.add(t);\n\t\t}\n\t\treturn tasks;\n\t}\n\n\t@MediumTest\n\tpublic void testTaskListURIs() {\n\t\tfinal TaskList list = getNewList();\n\t\tassertUriReturnsResult(TaskList.URI, TaskList.Columns.FIELDS);\n\t\tassertUriReturnsResult(TaskList.URI_WITH_COUNT, TaskList.Columns.FIELDS);\n\t\tlist.delete(mContext);\n\t}\n\n\t@MediumTest\n\tpublic void testTaskURIs() {\n\t\tfinal TaskList list = getNewList();\n\t\tfinal int taskCount = 5;\n\t\tfinal List<Task> tasks = insertSomeTasks(list, taskCount);\n\n\t\tassertUriReturnsResult(Task.URI, Task.Columns.FIELDS);\n\n\t\t// maybe the next line call to assertUriReturnsResult() fails due to timing issues ?\n\t\tSystemClock.sleep(500);\n\n\t\t// Sectioned Date query\n\t\tassertUriReturnsResult(\n\t\t\t\tTask.URI_SECTIONED_BY_DATE,\n\t\t\t\tTask.Columns.FIELDS,\n\t\t\t\t// see issue #525: on some android devices, \"dblist\" is a column of type BLOB,\n\t\t\t\t// but we expect it to always be INTEGER. On older devices this cast is redundant,\n\t\t\t\t// but on newer OS versions it fixes the bug when selecting notes by due date\n\t\t\t\t\"CAST(\" + Task.Columns.DBLIST + \" AS INTEGER) IS ?\",\n\t\t\t\tnew String[] { Long.toString(list._id) },\n\t\t\t\ttaskCount + 1);\n\n\t\t// History query\n\t\tTask t = tasks.get(0);\n\t\tfinal int histCount = 22;\n\t\tfor (int i = 0; i < 22; i++) {\n\t\t\t// edit the note & save it\n\t\t\tt.title += \" hist\" + i;\n\t\t\tt.save(mContext);\n\t\t}\n\t\t// Should return insert (1) + update count (histCount)\n\t\tassertUriReturnsResult(Task.URI_TASK_HISTORY,\n\t\t\t\tTask.Columns.HISTORY_COLUMNS, Task.Columns.HIST_TASK_ID\n\t\t\t\t\t\t+ \" IS ?\", new String[] { Long.toString(t._id) },\n\t\t\t\thistCount + 1);\n\n\t\t// TODO remember legacy uris\n\t\t// TODO need a projection mapper\n\t\t// assertUriReturnsResult(LegacyDBHelper.NotePad.Notes.CONTENT_URI,\n\t\t// new String[] { LegacyDBHelper.NotePad.Notes.COLUMN_NAME_TITLE });\n\t\t// assertUriReturnsResult(\n\t\t// LegacyDBHelper.NotePad.Notes.CONTENT_VISIBLE_URI,\n\t\t// new String[] { LegacyDBHelper.NotePad.Notes.COLUMN_NAME_TITLE });\n\n\t\tlist.delete(mContext);\n\n\t\t// Should return insert NOTHING since it should have been deleted\n\t\tassertUriReturnsResult(Task.URI_TASK_HISTORY,\n\t\t\t\tTask.Columns.HISTORY_COLUMNS, Task.Columns.HIST_TASK_ID\n\t\t\t\t\t\t+ \" IS ?\", new String[] { Long.toString(t._id) }, 0);\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBUpgradeTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.provider.BaseColumns;\n\nimport androidx.test.filters.MediumTest;\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.notepad.database.DatabaseHandler;\nimport com.nononsenseapps.notepad.database.LegacyDBHelper;\nimport com.nononsenseapps.notepad.database.LegacyDBHelper.NotePad;\nimport com.nononsenseapps.notepad.database.Notification;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport junit.framework.TestCase;\n\npublic class DBUpgradeTest extends TestCase {\n\tstatic final String PREFIX = \"dbupgrade_test_\";\n\n\tfinal String aTime = \"2013-03-23T02:43:35.000Z\";\n\tfinal String anId = \"MDIwMzMwNjA0MjM5MzQ4MzIzMjU6MDow\";\n\tfinal String anAccount = \"fake@account.com\";\n\n\tfinal int numOfLegacyLists = 2;\n\tfinal int numOfLegacyNotes = 4;\n\n\tprivate Context context;\n\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tcontext = InstrumentationRegistry.getInstrumentation().getTargetContext();\n\t}\n\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t}\n\n\tprivate void createTables(final SQLiteDatabase legacyDB) {\n\t\t// Lists\n\t\tlegacyDB.execSQL(\"CREATE TABLE \" + NotePad.Lists.TABLE_NAME + \" (\"\n\t\t\t\t+ BaseColumns._ID + \" INTEGER PRIMARY KEY,\"\n\t\t\t\t+ NotePad.Lists.COLUMN_NAME_TITLE\n\t\t\t\t+ \" TEXT DEFAULT '' NOT NULL,\"\n\t\t\t\t+ NotePad.Lists.COLUMN_NAME_MODIFIED\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\"\n\t\t\t\t+ NotePad.Lists.COLUMN_NAME_MODIFICATION_DATE\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\"\n\t\t\t\t+ NotePad.Lists.COLUMN_NAME_DELETED\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL\" + \");\");\n\n\t\tlegacyDB.execSQL(\"CREATE TABLE \" + NotePad.GTaskLists.TABLE_NAME + \" (\"\n\t\t\t\t+ BaseColumns._ID + \" INTEGER PRIMARY KEY,\"\n\t\t\t\t+ NotePad.GTaskLists.COLUMN_NAME_DB_ID\n\t\t\t\t+ \" INTEGER UNIQUE NOT NULL REFERENCES \"\n\t\t\t\t+ NotePad.Lists.TABLE_NAME + \",\"\n\t\t\t\t+ NotePad.GTaskLists.COLUMN_NAME_GTASKS_ID\n\t\t\t\t+ \" INTEGER NOT NULL,\"\n\t\t\t\t+ NotePad.GTaskLists.COLUMN_NAME_GOOGLE_ACCOUNT\n\t\t\t\t+ \" INTEGER NOT NULL,\" + NotePad.GTaskLists.COLUMN_NAME_UPDATED\n\t\t\t\t+ \" TEXT,\" + NotePad.GTaskLists.COLUMN_NAME_ETAG + \" TEXT\"\n\t\t\t\t+ \");\");\n\n\t\t// Notes\n\t\tlegacyDB.execSQL(\"CREATE TABLE \" + NotePad.Notes.TABLE_NAME + \" (\"\n\t\t\t\t+ BaseColumns._ID + \" INTEGER PRIMARY KEY,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_TITLE\n\t\t\t\t+ \" TEXT DEFAULT '' NOT NULL,\" + NotePad.Notes.COLUMN_NAME_NOTE\n\t\t\t\t+ \" TEXT DEFAULT '' NOT NULL,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_CREATE_DATE\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_DUE_DATE + \" TEXT,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_LIST\n\t\t\t\t+ \" INTEGER NOT NULL REFERENCES \" + NotePad.Lists.TABLE_NAME\n\t\t\t\t+ \",\" + NotePad.Notes.COLUMN_NAME_GTASKS_STATUS\n\t\t\t\t+ \" TEXT NOT NULL,\" + NotePad.Notes.COLUMN_NAME_POSITION\n\t\t\t\t+ \" TEXT,\" + NotePad.Notes.COLUMN_NAME_HIDDEN\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_MODIFIED\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\"\n\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_INDENTLEVEL\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_POSSUBSORT\n\t\t\t\t+ \" TEXT DEFAULT '' NOT NULL,\"\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_LOCALHIDDEN + \" INTEGER DEFAULT 0,\"\n\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_PARENT + \" TEXT,\"\n\n\t\t\t\t+ NotePad.Notes.COLUMN_NAME_DELETED\n\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL\" + \");\");\n\n\t\tlegacyDB.execSQL(\"CREATE TABLE \" + NotePad.GTasks.TABLE_NAME + \" (\"\n\t\t\t\t+ BaseColumns._ID + \" INTEGER PRIMARY KEY,\"\n\t\t\t\t+ NotePad.GTasks.COLUMN_NAME_DB_ID\n\t\t\t\t+ \" INTEGER UNIQUE NOT NULL REFERENCES \" + NotePad.Notes.TABLE_NAME\n\t\t\t\t+ \",\" + NotePad.GTasks.COLUMN_NAME_GTASKS_ID\n\t\t\t\t+ \" INTEGER NOT NULL,\"\n\t\t\t\t+ NotePad.GTasks.COLUMN_NAME_GOOGLE_ACCOUNT\n\t\t\t\t+ \" INTEGER NOT NULL,\" + NotePad.GTasks.COLUMN_NAME_UPDATED\n\t\t\t\t+ \" TEXT,\" + NotePad.GTasks.COLUMN_NAME_ETAG + \" TEXT\" + \");\");\n\n\t\t// Notifications\n\t\tlegacyDB.execSQL(\"CREATE TABLE \" + NotePad.Notifications.TABLE_NAME\n\t\t\t\t+ \" (\" + NotePad.Notifications._ID + \" INTEGER PRIMARY KEY,\"\n\t\t\t\t+ NotePad.Notifications.COLUMN_NAME_TIME\n\t\t\t\t+ \" INTEGER NOT NULL DEFAULT 0,\"\n\t\t\t\t+ NotePad.Notifications.COLUMN_NAME_PERMANENT\n\t\t\t\t+ \" INTEGER NOT NULL DEFAULT 0,\"\n\t\t\t\t+ NotePad.Notifications.COLUMN_NAME_NOTEID + \" INTEGER,\"\n\t\t\t\t+ \"FOREIGN KEY(\" + NotePad.Notifications.COLUMN_NAME_NOTEID\n\t\t\t\t+ \") REFERENCES \" + NotePad.Notes.TABLE_NAME + \"(\"\n\t\t\t\t+ NotePad.Notes._ID + \") ON DELETE CASCADE\" + \");\");\n\t}\n\n\tprivate void initializeDB(final SQLiteDatabase legacyDB) {\n\n\t\tlegacyDB.beginTransaction();\n\t\t// Need to create the tables so we have something to test with.\n\t\tcreateTables(legacyDB);\n\n\t\t// Insert some lists, and some notes\n\t\tfinal ContentValues values = new ContentValues();\n\t\tfor (int i = 0; i < numOfLegacyLists; i++) {\n\t\t\tvalues.clear();\n\t\t\t// One plain\n\t\t\tvalues.put(LegacyDBHelper.NotePad.Lists.COLUMN_NAME_TITLE, \"List\"\n\t\t\t\t\t+ i);\n\t\t\tvalues.put(LegacyDBHelper.NotePad.Lists.COLUMN_NAME_MODIFIED, 1);\n\t\t\tvalues.put(LegacyDBHelper.NotePad.Lists.COLUMN_NAME_DELETED, 0);\n\n\t\t\tfinal long listId = legacyDB.insert(\n\t\t\t\t\tLegacyDBHelper.NotePad.Lists.TABLE_NAME, null, values);\n\n\t\t\tassertTrue(\"Failed to insert legacy test list: \" + listId,\n\t\t\t\t\tlistId > 0);\n\n\t\t\tlong gtasklistid = -1;\n\t\t\t// One with google id\n\t\t\tif (i % 2 == 0) {\n\t\t\t\tvalues.clear();\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_DB_ID,\n\t\t\t\t\t\tlistId);\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_GOOGLE_ACCOUNT,\n\t\t\t\t\t\tanAccount);\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_GTASKS_ID,\n\t\t\t\t\t\tanId);\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_UPDATED,\n\t\t\t\t\t\taTime);\n\n\t\t\t\tgtasklistid = legacyDB.insert(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.GTaskLists.TABLE_NAME, null,\n\t\t\t\t\t\tvalues);\n\n\t\t\t\tassertTrue(\n\t\t\t\t\t\t\"Failed to insert google dummy list: \" + gtasklistid,\n\t\t\t\t\t\tgtasklistid > 0);\n\t\t\t}\n\n\t\t\t// Insert notes\n\t\t\tfor (int j = 0; j < numOfLegacyNotes; j++) {\n\t\t\t\tvalues.clear();\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_TITLE,\n\t\t\t\t\t\t\"default\" + j);\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_NOTE,\n\t\t\t\t\t\t\"defaulttext\");\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_MODIFIED, 1);\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_DELETED, 0);\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_LIST,\n\t\t\t\t\t\tlistId);\n\n\t\t\t\t// Gets the current system time in milliseconds\n\t\t\t\tLong now = System.currentTimeMillis();\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notes.COLUMN_NAME_CREATE_DATE,\n\t\t\t\t\t\tnow);\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE,\n\t\t\t\t\t\tnow);\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_DUE_DATE,\n\t\t\t\t\t\t\"\");\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notes.COLUMN_NAME_GTASKS_STATUS,\n\t\t\t\t\t\t\"needsAction\");\n\n\t\t\t\tvalues.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_POSSUBSORT,\n\t\t\t\t\t\t\"\");\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notes.COLUMN_NAME_INDENTLEVEL, 0);\n\n\t\t\t\tfinal long noteId = legacyDB.insert(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notes.TABLE_NAME, null, values);\n\n\t\t\t\tassertTrue(\"Note insertion should not fail\", noteId > 0);\n\t\t\t\tif (gtasklistid > -1) {\n\t\t\t\t\t// Give SOME of the notes google ids\n\t\t\t\t\tif (j % 2 == 0) {\n\t\t\t\t\t\tvalues.clear();\n\t\t\t\t\t\tvalues.put(\n\t\t\t\t\t\t\t\tLegacyDBHelper.NotePad.GTasks.COLUMN_NAME_DB_ID,\n\t\t\t\t\t\t\t\tnoteId);\n\t\t\t\t\t\tvalues.put(\n\t\t\t\t\t\t\t\tLegacyDBHelper.NotePad.GTasks.COLUMN_NAME_GOOGLE_ACCOUNT,\n\t\t\t\t\t\t\t\tanAccount);\n\t\t\t\t\t\tvalues.put(\n\t\t\t\t\t\t\t\tLegacyDBHelper.NotePad.GTasks.COLUMN_NAME_GTASKS_ID,\n\t\t\t\t\t\t\t\tanId + j);\n\t\t\t\t\t\tvalues.put(\n\t\t\t\t\t\t\t\tLegacyDBHelper.NotePad.GTasks.COLUMN_NAME_UPDATED,\n\t\t\t\t\t\t\t\taTime);\n\n\t\t\t\t\t\tfinal long gtaskid = legacyDB.insert(\n\t\t\t\t\t\t\t\tLegacyDBHelper.NotePad.GTasks.TABLE_NAME, null,\n\t\t\t\t\t\t\t\tvalues);\n\t\t\t\t\t\tassertTrue(\"Gtask insert should not fail\", gtaskid > 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Give all a notification\n\t\t\t\tvalues.clear();\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notifications.COLUMN_NAME_NOTEID,\n\t\t\t\t\t\tnoteId);\n\t\t\t\tvalues.put(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notifications.COLUMN_NAME_TIME,\n\t\t\t\t\t\tSystem.currentTimeMillis());\n\n\t\t\t\tfinal long notId = legacyDB.insert(\n\t\t\t\t\t\tLegacyDBHelper.NotePad.Notifications.TABLE_NAME, null,\n\t\t\t\t\t\tvalues);\n\t\t\t\tassertTrue(\"legacy notificaiton insert failed\", notId > 0);\n\t\t\t}\n\t\t}\n\n\t\tlegacyDB.setTransactionSuccessful();\n\t\tlegacyDB.endTransaction();\n\t}\n\n\t@MediumTest\n\tpublic void testExistingUpgrade() {\n\t\t// First delete test databases if they exist\n\t\tcontext.deleteDatabase(PREFIX + LegacyDBHelper.LEGACY_DATABASE_NAME);\n\t\tcontext.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME);\n\n\t\tfinal SQLiteDatabase legacyDB = new LegacyDBHelper(context, PREFIX)\n\t\t\t\t.getWritableDatabase();\n\t\tinitializeDB(legacyDB);\n\n\t\t// Check that things exist\n\t\tCursor c = DatabaseHandler.getLegacyLists(legacyDB);\n\n\t\tassertEquals(\"LegacyDB not correct for tests\", numOfLegacyLists,\n\t\t\t\tc.getCount());\n\t\tc.close();\n\n\t\tc = DatabaseHandler.getLegacyNotes(legacyDB);\n\t\tassertEquals(\"LegacyDB not correct for tests\", numOfLegacyLists\n\t\t\t\t* numOfLegacyNotes, c.getCount());\n\t\tc.close();\n\n\t\tc = DatabaseHandler.getLegacyNotifications(legacyDB);\n\t\tassertEquals(\"LegacyDB not correct for tests\", numOfLegacyLists\n\t\t\t\t* numOfLegacyNotes, c.getCount());\n\t\tc.close();\n\n\t\t// Check that new database correctly converts old\n\t\tfinal SQLiteDatabase db = new DatabaseHandler(context, PREFIX).getReadableDatabase();\n\n\t\tc = db.query(TaskList.TABLE_NAME, TaskList.Columns.FIELDS, null, null,\n\t\t\t\tnull, null, null);\n\t\tassertEquals(\"Unexpected amount of lists returned\", numOfLegacyLists,\n\t\t\t\tc.getCount());\n\n\t\t// TODO Examine details\n\t\tc.close();\n\n\t\tc = db.query(Task.TABLE_NAME, Task.Columns.FIELDS, null, null, null,\n\t\t\t\tnull, null);\n\t\tassertEquals(\"Incorrect number of notes converted\", numOfLegacyLists\n\t\t\t\t* numOfLegacyNotes, c.getCount());\n\n\t\t// TODO examine details\n\t\tc.close();\n\n\t\tc = db.query(Notification.TABLE_NAME, Notification.Columns.FIELDS,\n\t\t\t\tnull, null, null, null, null);\n\t\tassertEquals(\"Incorrect number of notifications converted\",\n\t\t\t\tnumOfLegacyLists * numOfLegacyNotes, c.getCount());\n\t\t// TODO examine details\n\t\tc.close();\n\n\t\tdb.close();\n\t\tlegacyDB.close();\n\n\t\tassertTrue(\n\t\t\t\t\"Could not delete database\",\n\t\t\t\tcontext.deleteDatabase(PREFIX\n\t\t\t\t\t\t+ LegacyDBHelper.LEGACY_DATABASE_NAME));\n\t\tassertTrue(\"Could not delete database\",\n\t\t\t\tcontext.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME));\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DaoTaskTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertNotEquals;\n\nimport androidx.test.filters.MediumTest;\n\nimport com.nononsenseapps.notepad.database.Task;\n\nimport junit.framework.TestCase;\n\npublic class DaoTaskTest extends TestCase {\n\n\tprivate void copyValsFromTo(final Task task1, final Task task2) {\n\t\ttask2.title = task1.title;\n\t\ttask2.note = task1.note;\n\t\ttask2.completed = task1.completed;\n\t\ttask2.due = task1.due;\n\t}\n\n\t@MediumTest\n\tpublic void testTask() {\n\t\tfinal Task task1 = new Task();\n\t\ttask1.title = \"title1\";\n\t\ttask1.note = \"note1\";\n\n\t\t// Equals method should be true for these\n\t\tassertEquals(\"Task should equal itself!\", task1, task1);\n\n\t\ttask1.due = 924592L;\n\t\tassertEquals(\"Task should equal itself!\", task1, task1);\n\n\t\ttask1.completed = 230456L;\n\t\tassertEquals(\"Task should equal itself!\", task1, task1);\n\n\t\t// Create copy\n\t\tfinal Task task2 = new Task();\n\t\tcopyValsFromTo(task1, task2);\n\n\t\tassertTrue((task1.title != null && task1.title.equals(task2.title)));\n\t\tassertTrue((task1.note != null && task1.note.equals(task2.note)));\n\t\tassertEquals(task1.due, task2.due);\n\t\tassertTrue(((task1.completed != null) == (task2.completed != null)));\n\n\t\tassertEquals(\"Task1 should equal task2!\", task1, task2);\n\n\t\t// Completed should only care about null status\n\t\ttask2.completed = 9272958113551L;\n\t\tassertEquals(\"Completed should only care about null values\", task1, task2);\n\n\t\t// Should all fail\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.title = \"badfa\";\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.note = \"badfa\";\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.due = 29037572395L;\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.completed = null;\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.due = null;\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.title = \"badfa\";\n\t\ttask2.note = \"asdfal\";\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.title = \"badfa\";\n\t\ttask2.due = null;\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.note = \"badfa\";\n\t\ttask2.due = 292374522222L;\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.title = \"badfa\";\n\t\ttask2.note = \"asdf\";\n\t\ttask2.due = null;\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\n\t\tcopyValsFromTo(task1, task2);\n\t\ttask2.title = \"badfa\";\n\t\ttask2.note = \"asdf\";\n\t\ttask2.due = null;\n\t\ttask2.completed = null;\n\t\tassertNotEquals(\"Task1 should not equal task2!\", task1, task2);\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DashClockSettingsTest.java",
    "content": "/*\n * Copyright (c) 2014 Jonas Kalderstam.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertNotNull;\n\nimport androidx.test.rule.ActivityTestRule;\n\nimport com.nononsenseapps.notepad.dashclock.DashclockPrefActivity;\n\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Verify that the activity opens OK on any screensize.\n */\npublic class DashClockSettingsTest {\n\n\t// the replacement, ActivityScenarioRule does not work\n\t@SuppressWarnings(\"deprecation\")\n\t@Rule\n\tpublic final ActivityTestRule<DashclockPrefActivity> mActivityRule\n\t\t\t= new ActivityTestRule<>(DashclockPrefActivity.class, false);\n\n\t@Test\n\tpublic void testLoadOK() {\n\t\tassertNotNull(mActivityRule.getActivity());\n\t\tHelper.takeScreenshot(\"Activity_loaded\");\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DateTimeTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.nononsenseapps.notepad.database.Task;\n\nimport junit.framework.TestCase;\n\n/**\n * Tests related to the Date and Time formats\n */\npublic class DateTimeTest extends TestCase {\n\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t}\n\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t}\n\n\t@SmallTest\n\tpublic void test_setAsCompleted() {\n\t\tTask t = new Task();\n\t\tt.setAsCompletedForLegacy(); // <-- see its comments\n\t\tassertTrue(t.completed > 0);\n\t}\n\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskDetailTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\nimport androidx.fragment.app.Fragment;\n\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\nimport com.nononsenseapps.notepad.espresso_tests.BaseTestClass;\nimport com.nononsenseapps.notepad.espresso_tests.EspressoHelper;\n\nimport org.junit.Test;\n\n\npublic class FragmentTaskDetailTest extends BaseTestClass {\n\n\t@Test\n\tpublic void testFragmentLoaded() {\n\n\t\tEspressoHelper.hideShowCaseViewIfShown();\n\t\tEspressoHelper.createNoteWithName(\"test note content\");\n\n\t\tFragment fragment = mActRule\n\t\t\t\t.getActivity()\n\t\t\t\t.getSupportFragmentManager()\n\t\t\t\t.findFragmentByTag(ActivityMain_.DETAILTAG);\n\t\tassertNotNull(\"Editor should NOT be null\", fragment);\n\t\tassertTrue(\"Editor should be visible\",\n\t\t\t\tfragment.isAdded() && fragment.isVisible());\n\n\t\tHelper.takeScreenshot(\"Editor_loaded\");\n\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\nimport android.widget.ListView;\n\nimport androidx.fragment.app.Fragment;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.rule.ActivityTestRule;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\n\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class FragmentTaskListsTest {\n\n\t// the replacement, ActivityScenarioRule does not work\n\t@SuppressWarnings(\"deprecation\")\n\t@Rule\n\tpublic final ActivityTestRule<ActivityMain_> mActivityRule\n\t\t\t= new ActivityTestRule<>(ActivityMain_.class, false);\n\n\t@Test\n\tpublic void testSanity() {\n\t\tassertEquals(\"This should succeed\", 1, 1);\n\t\tassertNotNull(\"Fragment1-holder should always be present\",\n\t\t\t\tmActivityRule.getActivity().findViewById(R.id.fragment1));\n\t}\n\n\t@Test\n\tpublic void testFragmentLoaded() {\n\t\tInstrumentationRegistry.getInstrumentation().waitForIdleSync();\n\t\tassertNotNull(mActivityRule.getActivity());\n\n\t\tFragment listPagerFragment = mActivityRule\n\t\t\t\t.getActivity()\n\t\t\t\t.getSupportFragmentManager()\n\t\t\t\t.findFragmentByTag(ActivityMain_.LISTPAGERTAG);\n\n\t\tassertNotNull(\"List pager fragment should not be null\", listPagerFragment);\n\t\tassertTrue(\"List pager fragment should be visible\",\n\t\t\t\tlistPagerFragment.isAdded() && listPagerFragment.isVisible());\n\n\t\tListView taskList = listPagerFragment\n\t\t\t\t.getView()\n\t\t\t\t.findViewById(android.R.id.list);\n\n\t\tassertNotNull(\"Could not find the list!\", taskList);\n\n\t\tHelper.takeScreenshot(\"List_loaded\");\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsViewPagerTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport androidx.test.rule.ActivityTestRule;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\n\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class FragmentTaskListsViewPagerTest {\n\n\t// the replacement, ActivityScenarioRule does not work\n\t@SuppressWarnings(\"deprecation\")\n\t@Rule\n\tpublic final ActivityTestRule<ActivityMain_> mActivityRule\n\t\t\t= new ActivityTestRule<>(ActivityMain_.class, false);\n\n\t@Test\n\tpublic void testSanity() {\n\t\tassertEquals(\"This should succeed\", 1, 1);\n\t\tassertNotNull(\"Error in the activityrule\", mActivityRule.getActivity());\n\t\tassertNotNull(\"Fragment1-holder should always be present\",\n\t\t\t\tmActivityRule.getActivity().findViewById(R.id.fragment1));\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/Helper.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.graphics.Bitmap;\n\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n\npublic class Helper {\n\n\tprivate static Task getATask(final Context context) {\n\t\tCursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(Task.URI, Task.Columns.FIELDS, null, null, null);\n\t\tTask result = null;\n\t\tif (c.moveToFirst())\n\t\t\tresult = new Task(c);\n\t\treturn result;\n\t}\n\n\tprivate static TaskList getATaskList(final Context context) {\n\t\tCursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(TaskList.URI, TaskList.Columns.FIELDS, null, null, null);\n\t\tTaskList result = null;\n\t\tif (c.moveToFirst())\n\t\t\tresult = new TaskList(c);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Takes a screenshots and saves it as \"filename\", for example\n\t * /storage/emulated/0/Android/data/com.nononsenseapps.notepad/files/screenshots/fileName.png\n\t * This is mandatory in new android versions, since it's the only folder we can easily write to\n\t */\n\tpublic static void takeScreenshot(String fileName) {\n\t\t// wait a second for the activity to load.\n\t\ttry {Thread.sleep(1000);} catch (InterruptedException ignored) {}\n\n\t\tvar tool = InstrumentationRegistry.getInstrumentation();\n\t\tBitmap bmp = tool.getUiAutomation().takeScreenshot();\n\n\t\tFile dir = tool.getTargetContext().getExternalFilesDir(\"screenshots\");\n\t\tif (!dir.exists()) {\n\t\t\tassertTrue(\"Could not create directory\", dir.mkdirs());\n\t\t}\n\n\t\t// the png file\n\t\tvar file = new File(dir, fileName + \".png\");\n\n\t\ttry (var out = new FileOutputStream(file.getAbsolutePath())) {\n\t\t\tbmp.compress(Bitmap.CompressFormat.PNG, 100, out);\n\t\t} catch (IOException e) {\n\t\t\tfail(\"Could not save png screenshot\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/OrgSyncTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.database.Cursor;\n\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.helpers.FileHelper;\nimport com.nononsenseapps.notepad.database.RemoteTask;\nimport com.nononsenseapps.notepad.database.RemoteTaskList;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.sync.orgsync.OrgConverter;\nimport com.nononsenseapps.notepad.sync.orgsync.SDSynchronizer;\n\nimport org.cowboyprogrammer.org.OrgFile;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashSet;\n\n/**\n * Test the synchronizer code.\n * Methods starting with 'testFresh' are meant to be reused in higher-order\n * tests.\n */\npublic class OrgSyncTest {\n\n\tprivate static final String ACCOUNT = \"bobtester\";\n\tprivate static String DIR;\n\n\t/**\n\t * @return a Context to use during testing. It reports the same packagename of the\n\t * app: com.nononsenseapps.notepad\n\t */\n\tprivate static Context getTheContext() {\n\t\t// VERY IMPORTANT: it should NOT be .getContext(), because that one uses the namespace\n\t\t// com.nononsenseapps.notepad.test which saves the files on a wrong path, which makes\n\t\t// every test in this class fail! ApplicationProvider.getApplicationContext() seems fine.\n\t\treturn InstrumentationRegistry.getInstrumentation().getTargetContext();\n\t}\n\n\t@Before\n\tpublic void setUp() {\n\t\tDIR = FileHelper.getUserSelectedOrgDir(getTheContext());\n\t\tassertNotNull(DIR);\n\t\tvar d = new File(DIR);\n\t\tif (!d.exists()) assertTrue(d.mkdirs());\n\n\t\t// since the app starts with a default, \"Welcome!\" note, we must clear all notes,\n\t\t// (and tasks, org files, ...) to make sure the tests in this class can complete\n\t\ttearDown();\n\t}\n\n\t/**\n\t * Performs a cleanup of the app's data and org files\n\t */\n\t@After\n\tpublic void tearDown() {\n\t\tContentResolver resolver = getTheContext().getContentResolver();\n\t\tresolver.delete(TaskList.URI, null, null);\n\t\tresolver.delete(Task.URI, null, null);\n\t\tresolver.delete(RemoteTaskList.URI, null, null);\n\t\tresolver.delete(RemoteTask.URI, null, null);\n\n\t\tFile d = new File(DIR);\n\t\tFile[] filesInFolder = d.listFiles();\n\t\tassertNotNull(\"Can not get files in folder\", filesInFolder);\n\t\tfor (File f : filesInFolder) {\n\t\t\tFileHelper.tryDeleteFile(f, getTheContext());\n\t\t}\n\t}\n\n\tpublic ArrayList<TaskList> getTaskLists() {\n\t\tContentResolver resolver = getTheContext().getContentResolver();\n\t\tCursor c = resolver.query(TaskList.URI, TaskList.Columns\n\t\t\t\t.FIELDS, null, null, null);\n\n\t\tArrayList<TaskList> result = new ArrayList<>();\n\t\twhile (c.moveToNext()) {\n\t\t\tresult.add(new TaskList(c));\n\t\t}\n\t\tc.close();\n\n\t\treturn result;\n\t}\n\n\tpublic ArrayList<Task> getTasks(final long listid) {\n\t\tContentResolver resolver = getTheContext().getContentResolver();\n\t\tCursor c = resolver.query(Task.URI, Task.Columns\n\t\t\t\t\t\t.FIELDS, Task.Columns.DBLIST + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(listid) }, null\n\t\t);\n\n\t\tArrayList<Task> result = new ArrayList<>();\n\t\twhile (c.moveToNext()) {\n\t\t\tresult.add(new Task(c));\n\t\t}\n\t\tc.close();\n\n\t\treturn result;\n\t}\n\n\tpublic ArrayList<RemoteTaskList> getRemoteTaskLists() {\n\t\tContentResolver resolver = getTheContext().getContentResolver();\n\t\tCursor c = resolver.query(RemoteTaskList.URI, RemoteTaskList.Columns\n\t\t\t\t\t\t.FIELDS, RemoteTaskList.Columns.ACCOUNT + \" IS ?\",\n\t\t\t\tnew String[] { ACCOUNT }, null\n\t\t);\n\n\t\tArrayList<RemoteTaskList> result = new ArrayList<>();\n\t\twhile (c.moveToNext()) {\n\t\t\tresult.add(new RemoteTaskList(c));\n\t\t}\n\t\tc.close();\n\n\t\treturn result;\n\t}\n\n\tpublic ArrayList<RemoteTask> getRemoteTasks() {\n\t\tContentResolver resolver = getTheContext().getContentResolver();\n\t\tCursor c = resolver.query(RemoteTask.URI, RemoteTask.Columns\n\t\t\t\t\t\t.FIELDS, RemoteTask.Columns.ACCOUNT + \" IS ?\",\n\t\t\t\tnew String[] { ACCOUNT }, null\n\t\t);\n\n\t\tArrayList<RemoteTask> result = new ArrayList<>();\n\t\twhile (c.moveToNext()) {\n\t\t\tresult.add(new RemoteTask(c));\n\t\t}\n\t\tc.close();\n\n\t\treturn result;\n\t}\n\n\t@Test\n\tpublic void testPass() {\n\t\t// This always passes\n\t\tassertTrue(true);\n\t}\n\n\t@Test\n\tpublic void testTester() {\n\t\tTestSynchronizer tester = new TestSynchronizer(getTheContext());\n\t\tassertTrue(tester.isConfigured());\n\t}\n\n\t/**\n\t * End result: synced state of one tasklist with two tasks.\n\t * Tested flow branches:\n\t * - Lists: Create file\n\t * - Tasks: Create node\n\t */\n\t@Test\n\tpublic void testFreshSimple() {\n\t\t// First create a list with 2 tasks\n\t\tTaskList list = new TaskList();\n\t\tlist.title = \"TestList\";\n\t\tlist.save(getTheContext());\n\t\tassertTrue(list._id > 0);\n\n\t\tfinal int taskCount = 2;\n\t\tfor (int i = 0; i < taskCount; i++) {\n\t\t\tTask t = new Task();\n\t\t\tt.dblist = list._id;\n\t\t\tt.title = \"Task\" + i;\n\t\t\tt.note = \"A body for the task\";\n\t\t\tt.save(getTheContext());\n\t\t\tassertTrue(t._id > 0);\n\t\t}\n\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// See the result\n\t\tHashSet<String> filenames = synchronizer.getRemoteFilenames();\n\n\t\tassertEquals(\"Only one list was created.\", 1, filenames.size());\n\n\t\tString filename = null;\n\t\tfor (String f : filenames) {\n\t\t\tfilename = f;\n\t\t}\n\n\t\tassertEquals(\"Wrong filename\", list.title + \".org\", filename);\n\n\t\t// Check that the database is correct\n\t\tArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should only be one RemoteList!\", 1, remoteLists.size());\n\n\t\tArrayList<RemoteTask> remoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly 2 RemoteTasks\", taskCount, remoteTasks.size());\n\t\tlong lastDbid = -1;\n\t\tfor (int i = 1; i < remoteTasks.size() + 1; i++) {\n\t\t\tRemoteTask r = remoteTasks.remove(i - 1);\n\t\t\t// Check for duplicates\n\t\t\tassertEquals(\"Id is not correct\", i, r._id);\n\t\t\tassertTrue(lastDbid != r.dbid);\n\t\t\tlastDbid = r.dbid;\n\t\t}\n\t}\n\n\tpublic void syncAndAssertNothingChanged(final int taskCount) {\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// It should NOT have written to disk at all\n\t\tassertEquals(\"No changes should not be written!\", 0,\n\t\t\t\tsynchronizer.getPutRemoteCount());\n\n\t\t// Check that the database is still correct\n\t\tArrayList<TaskList> lists = getTaskLists();\n\t\tassertEquals(\"Should only be one list\", 1, lists.size());\n\n\t\tArrayList<Task> tasks = getTasks(lists.get(0)._id);\n\t\tassertEquals(\"Should be only 2 tasks in list\", taskCount, tasks.size());\n\n\t\tArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should only be one RemoteList!\", 1, remoteLists.size());\n\n\t\tArrayList<RemoteTask> remoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly 2 RemoteTasks\", taskCount, remoteTasks.size());\n\t\tlong lastDbid = -1;\n\t\tfor (int i = 1; i < remoteTasks.size() + 1; i++) {\n\t\t\tRemoteTask r = remoteTasks.get(i - 1);\n\t\t\t// Check for duplicates\n\t\t\tassertEquals(\"Id is not correct\", i, r._id);\n\t\t\tassertTrue(lastDbid != r.dbid);\n\t\t\tlastDbid = r.dbid;\n\t\t}\n\t}\n\n\t/**\n\t * Nothing has changed here.\n\t * Tested flow branches:\n\t * - Lists: Update Merge\n\t * - Tasks: Update Merge\n\t */\n\t@Test\n\tpublic void testNothingNew() {\n\t\tfinal int taskCount = 2;\n\t\ttestFreshSimple();\n\n\t\tsyncAndAssertNothingChanged(taskCount);\n\t\tsyncAndAssertNothingChanged(taskCount);\n\t\tsyncAndAssertNothingChanged(taskCount);\n\t\tsyncAndAssertNothingChanged(taskCount);\n\t}\n\n\t/**\n\t * Having two lists with the same name is possible in the app,\n\t * but obviously impossible at the filesystem level.\n\t * Tested flow branches:\n\t * - Lists: Create file\n\t */\n\t@Test\n\tpublic void testDuplicateName() {\n\t\t// Create first list\n\t\tTaskList list1 = new TaskList();\n\t\tlist1.title = \"TestList\";\n\t\tlist1.save(getTheContext());\n\t\tassertTrue(list1._id > 0);\n\n\t\t// Create second list\n\t\tTaskList list2 = new TaskList();\n\t\tlist2.title = \"TestList\";\n\t\tlist2.save(getTheContext());\n\t\tassertTrue(list2._id > 0);\n\n\t\t// Sync it\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Make sure the second one was renamed!\n\t\tfor (TaskList tl : getTaskLists()) {\n\t\t\tif (tl._id == list1._id) {\n\t\t\t\tassertEquals(list1.title, tl.title);\n\t\t\t} else if (tl._id == list2._id) {\n\t\t\t\tassertEquals(\"List should have been renamed\",\n\t\t\t\t\t\tlist2.title + 1, tl.title);\n\t\t\t}\n\t\t}\n\n\t\tHashSet<String> filenames = synchronizer.getRemoteFilenames();\n\t\tassertEquals(2, filenames.size());\n\t\tassertTrue(filenames.contains(list1.title + \".org\"));\n\t\tassertTrue(filenames.contains(list2.title + 1 + \".org\"));\n\t}\n\n\t/**\n\t * Renaming a list in the app should rename the file.\n\t * Tested branches:\n\t * - Update list, renamed\n\t */\n\t@Test\n\tpublic void testRenamedList() {\n\t\t// Create first list\n\t\tTaskList list1 = new TaskList();\n\t\tlist1.title = \"TestList\";\n\t\tlist1.save(getTheContext());\n\t\tassertTrue(list1._id > 0);\n\n\t\t// Sync it\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\tFile org = new File(DIR, OrgConverter.getTitleAsFilename\n\t\t\t\t(list1));\n\t\t// Make sure original file is there\n\t\tassertTrue(org.exists());\n\n\t\t// Rename the list\n\t\tlist1.title = \"RenamedList\";\n\t\tlist1.save(getTheContext());\n\n\t\t// Sync it\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Make sure rename was successful\n\t\tassertFalse(org.exists());\n\n\t\tFile renamed = new File(DIR, OrgConverter.getTitleAsFilename\n\t\t\t\t(list1));\n\t\tassertTrue(renamed.exists());\n\t}\n\n\t/**\n\t * Deleting a list should delete the corresponding file and all tasks.\n\t * Tested branches:\n\t * - Delete File Db\n\t */\n\t@Test\n\tpublic void testDeletedList() {\n\t\t// Setup simple DB\n\t\tfinal int taskCount = 2;\n\t\ttestFreshSimple();\n\n\t\t// Delete list(s)\n\t\tFile file = null;\n\t\tArrayList<TaskList> lists = getTaskLists();\n\t\tfor (TaskList list : lists) {\n\t\t\tfile = new File(DIR, OrgConverter.getTitleAsFilename(list));\n\n\t\t\tlist.delete(getTheContext());\n\t\t}\n\n\t\tassertNotNull(file);\n\t\t// Make sure it exists at this point\n\t\tassertTrue(file.exists());\n\t\t// And that the database still has a record of it\n\t\tArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should be one RemoteList!\", 1, remoteLists.size());\n\n\t\tArrayList<RemoteTask> remoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly 2 RemoteTasks\", taskCount, remoteTasks.size());\n\n\t\t// Sync it again\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check that the database has removed it\n\t\tlists = getTaskLists();\n\t\tassertTrue(\"Should be no list\", lists.isEmpty());\n\n\t\tremoteLists = getRemoteTaskLists();\n\t\tassertTrue(\"Should be no RemoteList!\", remoteLists.isEmpty());\n\n\t\tremoteTasks = getRemoteTasks();\n\t\tassertTrue(\"Should be no RemoteTasks\", remoteTasks.isEmpty());\n\n\t\t// Make sure no file exists anymore\n\t\tassertFalse(file.exists());\n\t}\n\n\t/**\n\t * Test moving 1 task from List A to List B\n\t */\n\t@Test\n\tpublic void testMoveOne() {\n\t\t// First create Two lists\n\t\tTaskList listA = new TaskList();\n\t\tlistA.title = \"TestListA\";\n\t\tlistA.save(getTheContext());\n\t\tassertTrue(listA._id > 0);\n\n\t\tTaskList listB = new TaskList();\n\t\tlistB.title = \"TestListB\";\n\t\tlistB.save(getTheContext());\n\t\tassertTrue(listB._id > 0);\n\n\t\t// Add one task in ListA\n\t\tTask t = new Task();\n\t\tt.dblist = listA._id;\n\t\tt.title = \"Task\";\n\t\tt.note = \"A body for the task\";\n\t\tt.save(getTheContext());\n\t\tassertTrue(t._id > 0);\n\n\t\t// Sync it\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check state of sync\n\t\tArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should be two RemoteLists!\", 2, remoteLists.size());\n\n\t\tArrayList<RemoteTask> remoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly 1 RemoteTask\", 1, remoteTasks.size());\n\n\t\tassertEquals(\"RemoteTask is in wrong list!\", listA._id,\n\t\t\t\t(long) remoteTasks.get(0).listdbid);\n\n\t\t// Move the task\n\t\tt.dblist = listB._id;\n\t\tt.save(getTheContext());\n\n\t\t// Trigger should have deleted remotes now\n\t\tremoteTasks = getRemoteTasks();\n\t\tfor (RemoteTask rt : remoteTasks) {\n\t\t\tassertEquals(\"RemoteTask should be deleted after move before sync\", \"deleted\", rt.deleted);\n\t\t}\n\n\t\t// Sync it\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check state of sync\n\t\tremoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should be two RemoteLists after move!\", 2, remoteLists.size());\n\n\t\tremoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly 1 RemoteTask after move\", 1, remoteTasks.size());\n\n\t\tassertEquals(\"RemoteTask is in wrong list after move!\", listB._id,\n\t\t\t\t(long) remoteTasks.get(0).listdbid);\n\t}\n\n\t/**\n\t * Test moving 20 tasks from List A to List B\n\t */\n\t@Test\n\tpublic void testMoveMany() {\n\t\t// First create Two lists\n\t\tTaskList listA = new TaskList();\n\t\tlistA.title = \"TestListA\";\n\t\tlistA.save(getTheContext());\n\t\tassertTrue(listA._id > 0);\n\n\t\tTaskList listB = new TaskList();\n\t\tlistB.title = \"TestListB\";\n\t\tlistB.save(getTheContext());\n\t\tassertTrue(listB._id > 0);\n\n\t\tfinal int taskCount = 20;\n\t\tArrayList<Task> tasks = new ArrayList<>();\n\t\tfor (int i = 0; i < taskCount; i++) {\n\t\t\tTask t = new Task();\n\t\t\tt.dblist = listA._id;\n\t\t\tt.title = \"Task\" + i;\n\t\t\tt.note = \"A body for the task\";\n\t\t\tt.save(getTheContext());\n\t\t\tassertTrue(t._id > 0);\n\n\t\t\ttasks.add(t);\n\t\t}\n\n\t\t// Sync it\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check state of sync\n\t\tArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should be two RemoteLists!\", 2, remoteLists.size());\n\n\t\tArrayList<RemoteTask> remoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly x RemoteTask\", taskCount, remoteTasks.size());\n\n\t\tfor (RemoteTask remoteTask : remoteTasks) {\n\t\t\tassertEquals(\"RemoteTask is in wrong list!\", listA._id,\n\t\t\t\t\t(long) remoteTask.listdbid);\n\t\t}\n\n\t\t// Move the tasks\n\t\tfor (Task t : tasks) {\n\t\t\tt.dblist = listB._id;\n\t\t\tt.save(getTheContext());\n\t\t}\n\n\t\t// Trigger should have deleted remotes now\n\t\tremoteTasks = getRemoteTasks();\n\t\tfor (RemoteTask rt : remoteTasks) {\n\t\t\tassertEquals(\"RemoteTask should be deleted after move before sync\", \"deleted\", rt.deleted);\n\t\t}\n\n\t\t// Sync it\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check state of sync\n\t\tremoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should be two RemoteLists after move!\", 2, remoteLists.size());\n\n\t\tremoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly x RemoteTask after move and sync\", taskCount, remoteTasks.size());\n\n\t\tfor (RemoteTask remoteTask : remoteTasks) {\n\t\t\tassertEquals(\"RemoteTask is in wrong list after move!\", listB._id,\n\t\t\t\t\t(long) remoteTask.listdbid);\n\t\t}\n\t}\n\n\t/**\n\t * Test moving 12 tasks from List A to List B where there are 12 lists each with 20 tasks\n\t */\n\t@Test\n\tpublic void testMoveManyAmongMany() {\n\t\tfinal int listCount = 12;\n\t\tfinal int taskCount = 20;\n\t\tfinal int movedTaskCount = 12;\n\t\tArrayList<Task> tasksToMove = new ArrayList<>();\n\t\tTaskList listA = null, listB = null;\n\t\t// First create Lists\n\t\tfor (int listIndex = 0; listIndex < listCount; listIndex++) {\n\t\t\tTaskList list = new TaskList();\n\t\t\tlist.title = \"TestList\" + listIndex;\n\t\t\tlist.save(getTheContext());\n\t\t\tassertTrue(list._id > 0);\n\n\t\t\tif (listA == null)\n\t\t\t\tlistA = list;\n\t\t\telse if (listB == null)\n\t\t\t\tlistB = list;\n\n\t\t\tfor (int i = 0; i < taskCount; i++) {\n\t\t\t\tTask t = new Task();\n\t\t\t\tt.dblist = list._id;\n\t\t\t\tt.title = \"Task\" + listIndex + \".\" + i;\n\t\t\t\tt.note = \"A body for the task\";\n\t\t\t\tt.save(getTheContext());\n\t\t\t\tassertTrue(t._id > 0);\n\n\t\t\t\tif (tasksToMove.size() < movedTaskCount) {\n\t\t\t\t\ttasksToMove.add(t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Sync it\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check state of sync\n\t\tArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();\n\t\tassertEquals(\"Should be X RemoteLists!\", listCount, remoteLists.size());\n\n\t\tArrayList<RemoteTask> remoteTasks = getRemoteTasks();\n\t\tassertEquals(\"Should be exactly x RemoteTask\", taskCount * listCount, remoteTasks.size());\n\n\t\t// Move the tasks\n\t\tassertNotNull(listA);\n\t\tassertNotNull(listB);\n\t\tassertTrue(\"List A and B should be different!\", listA._id != listB._id);\n\t\tassertEquals(\"Expected something to move\", movedTaskCount, tasksToMove.size());\n\t\tfor (Task t : tasksToMove) {\n\t\t\tassertEquals(\"Expected task to be in list A!\", listA._id, (long) t.dblist);\n\t\t\tt.dblist = listB._id;\n\t\t\tt.save(getTheContext());\n\t\t}\n\n\t\t// Trigger should have deleted remotes now\n\t\tremoteTasks = getRemoteTasks();\n\t\tint deletecount = 0;\n\t\tint realcount = 0;\n\t\tfor (RemoteTask rt : remoteTasks) {\n\t\t\tif (\"deleted\".equals(rt.deleted)) {\n\t\t\t\tdeletecount += 1;\n\t\t\t} else {\n\t\t\t\trealcount += 1;\n\t\t\t}\n\t\t}\n\n\t\tassertEquals(\"Deleted remotetasks did not match\", movedTaskCount, deletecount);\n\t\tassertEquals(\"Remaining remotetasks did not match\", taskCount * listCount - movedTaskCount, realcount);\n\n\t\t// Sync it\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check state of sync\n\t\tArrayList<RemoteTaskList> remoteTaskLists = getRemoteTaskLists();\n\t\tremoteTasks = getRemoteTasks();\n\t\tdeletecount = 0;\n\t\trealcount = 0;\n\t\tfor (RemoteTask rt : remoteTasks) {\n\t\t\tif (\"deleted\".equals(rt.deleted)) {\n\t\t\t\tdeletecount += 1;\n\t\t\t} else {\n\t\t\t\trealcount += 1;\n\t\t\t}\n\t\t}\n\n\t\tassertEquals(\"Number of remote lits did not match\", listCount, remoteTaskLists.size());\n\t\tassertEquals(\"Deleted remotetasks did not match\", 0, deletecount);\n\t\tassertEquals(\"Remaining remotetasks did not match\", taskCount * listCount, realcount);\n\n\t\tint nowInB = 0;\n\t\tfor (RemoteTask remoteTask : remoteTasks) {\n\t\t\tassertNotEquals(\"deleted\", remoteTask.deleted);\n\t\t\tif (remoteTask.listdbid == listB._id) {\n\t\t\t\tnowInB += 1;\n\t\t\t}\n\t\t}\n\t\tassertEquals(\"RemoteTasks in b not expected count\", taskCount + movedTaskCount,\n\t\t\t\tnowInB);\n\n\t\t// Check same things for local tasks\n\t\tArrayList<TaskList> taskLists = getTaskLists();\n\t\tassertEquals(\"Number of lists did not match\", listCount, taskLists.size());\n\t\tfor (TaskList list : taskLists) {\n\t\t\tArrayList<Task> tasks = getTasks(list._id);\n\t\t\tif (listA._id == list._id) {\n\t\t\t\tassertEquals(\"Not expected count in A\", taskCount - movedTaskCount, tasks.size());\n\t\t\t} else if (listB._id == list._id) {\n\t\t\t\tassertEquals(\"Not expected count in B\", taskCount + movedTaskCount, tasks.size());\n\t\t\t} else {\n\t\t\t\tassertEquals(\"Not expected count in C->\", taskCount, tasks.size());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testFilenameWithSlash() {\n\t\t// Filenames with slashes are not permitted\n\t\tfinal TaskList lista = new TaskList();\n\t\tlista.title = \"Test/List/Slash/Name\";\n\t\tlista.save(getTheContext());\n\t\tassertTrue(lista._id > 0);\n\n\t\t// Sync it\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check contents after sync\n\t\tfinal TaskList listb = getTaskLists().get(0);\n\n\t\t// Should no longer have slashes in name\n\t\tassertFalse(listb.title.contains(\"/\"));\n\t\tassertEquals(\"Test_List_Slash_Name\", listb.title);\n\t}\n\n\t@Test\n\tpublic void testContentStability() {\n\t\t// Make sure content is not changed\n\t\t// Create list\n\t\tfinal TaskList lista = new TaskList();\n\t\tlista.title = \"TestList\";\n\t\tlista.save(getTheContext());\n\t\tassertTrue(lista._id > 0);\n\n\t\tfinal Task task1a = new Task();\n\t\ttask1a.title = \"The title1\";\n\t\ttask1a.note = \"A note without newline\";\n\t\ttask1a.dblist = lista._id;\n\t\ttask1a.save(getTheContext());\n\t\tassertTrue(task1a._id > 0);\n\n\t\tfinal Task task2a = new Task();\n\t\ttask2a.title = \"The title2\";\n\t\ttask2a.note = \"Another note\\non two lines\";\n\t\ttask2a.dblist = lista._id;\n\t\ttask2a.save(getTheContext());\n\t\tassertTrue(task2a._id > 0);\n\n\t\t// Sync it\n\t\tTestSynchronizer synchronizer = new TestSynchronizer(getTheContext());\n\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check contents after sync\n\t\tfinal TaskList listb = getTaskLists().get(0);\n\n\t\tassertEquals(lista.title, listb.title);\n\n\n\t\tfor (Task taskb : getTasks(listb._id)) {\n\t\t\tTask org;\n\t\t\tif (taskb._id == task1a._id) {\n\t\t\t\torg = task1a;\n\t\t\t} else {\n\t\t\t\torg = task2a;\n\t\t\t}\n\t\t\t// Compare title and note\n\t\t\tassertEquals(org.title, taskb.title);\n\t\t\tassertEquals(org.note, taskb.note);\n\t\t}\n\n\t\t// Sync it again\n\t\ttry {\n\t\t\tsynchronizer.fullSync();\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check contents after sync\n\t\tfinal TaskList listc = getTaskLists().get(0);\n\n\t\tassertEquals(lista.title, listc.title);\n\n\t\tfor (Task taskc : getTasks(listb._id)) {\n\t\t\tTask org;\n\t\t\tif (taskc._id == task1a._id) {\n\t\t\t\torg = task1a;\n\t\t\t} else {\n\t\t\t\torg = task2a;\n\t\t\t}\n\t\t\t// Compare title and note\n\t\t\tassertEquals(org.title, taskc.title);\n\t\t\tassertEquals(org.note, taskc.note);\n\t\t}\n\t}\n\n\tstatic class TestSynchronizer extends SDSynchronizer {\n\n\t\tprivate int putRemoteCount = 0;\n\n\t\tpublic TestSynchronizer(Context context) {\n\t\t\tsuper(context);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isConfigured() {\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * @return A unique name for this service. Should be descriptive, like\n\t\t * SDOrg or SSHOrg.\n\t\t */\n\t\t@Override\n\t\tpublic String getServiceName() {\n\t\t\treturn ACCOUNT;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getAccountName() {\n\t\t\treturn ACCOUNT;\n\t\t}\n\n\t\t/**\n\t\t * Replaces the file on the remote end with the given content.\n\t\t *\n\t\t * @param orgFile The file to save. Uses the filename stored in the object.\n\t\t */\n\t\t@Override\n\t\tpublic void putRemoteFile(OrgFile orgFile) throws IOException {\n\t\t\tputRemoteCount += 1;\n\t\t\tsuper.putRemoteFile(orgFile);\n\t\t}\n\n\t\tpublic int getPutRemoteCount() {\n\t\t\treturn putRemoteCount;\n\t\t}\n\n\t\tpublic void setPutRemoteCount(final int putRemoteCount) {\n\t\t\tthis.putRemoteCount = putRemoteCount;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/ProviderHelperTest.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertEquals;\n\nimport com.nononsenseapps.notepad.android.provider.ProviderHelper;\n\nimport org.junit.Test;\n\npublic class ProviderHelperTest {\n\n\t@Test\n\tpublic void testGetRelativePath() throws Exception {\n\t\tassertEquals(\"/foo/bar\",\n\t\t\t\tProviderHelper.getRelativePath(\"/ACTION/foo/bar\"));\n\t\tassertEquals(\"/foo/bar\",\n\t\t\t\tProviderHelper.getRelativePath(\"ACTION/foo/bar\"));\n\t\tassertEquals(\"/\",\n\t\t\t\tProviderHelper.getRelativePath(\"/Action\"));\n\t\tassertEquals(\"/\",\n\t\t\t\tProviderHelper.getRelativePath(\"Action\"));\n\t}\n\n\t@Test\n\tpublic void testFirstPart() throws Exception {\n\t\tassertEquals(\"\", ProviderHelper.firstPart(\"\"));\n\t\tassertEquals(\"foo\", ProviderHelper.firstPart(\"foo\"));\n\t\tassertEquals(\"foo\", ProviderHelper.firstPart(\"/foo\"));\n\t\tassertEquals(\"foo\", ProviderHelper.firstPart(\"/foo/bar\"));\n\t\tassertEquals(\"foo\", ProviderHelper.firstPart(\"foo/bar\"));\n\t}\n\n\t@Test\n\tpublic void testRestPart() throws Exception {\n\t\tassertEquals(\"\", ProviderHelper.restPart(\"foo\"));\n\t\tassertEquals(\"\", ProviderHelper.restPart(\"/foo\"));\n\t\tassertEquals(\"\", ProviderHelper.restPart(\"/foo/\"));\n\t\tassertEquals(\"bar/baz\", ProviderHelper.restPart(\"/foo/bar/baz\"));\n\t\tassertEquals(\"bar/baz\", ProviderHelper.restPart(\"foo/bar/baz\"));\n\t\tassertEquals(\"bar/baz\", ProviderHelper.restPart(\"/foo/bar/baz\"));\n\t}\n\n\t@Test\n\tpublic void testMatchPath() throws Exception {\n\t\t// Root cases\n\t\tassertEquals(ProviderHelper.URI_ROOT,\n\t\t\t\tProviderHelper.matchPath(null));\n\t\tassertEquals(ProviderHelper.URI_ROOT,\n\t\t\t\tProviderHelper.matchPath(\"\"));\n\t\tassertEquals(ProviderHelper.URI_ROOT,\n\t\t\t\tProviderHelper.matchPath(\"/\"));\n\n\t\t// List\n\t\tassertEquals(ProviderHelper.URI_LIST,\n\t\t\t\tProviderHelper.matchPath(\"/list\"));\n\t\tassertEquals(ProviderHelper.URI_LIST,\n\t\t\t\tProviderHelper.matchPath(\"/list/\"));\n\t\tassertEquals(ProviderHelper.URI_LIST,\n\t\t\t\tProviderHelper.matchPath(\"list/\"));\n\t\tassertEquals(ProviderHelper.URI_LIST,\n\t\t\t\tProviderHelper.matchPath(\"/list/\"));\n\n\t\tassertEquals(ProviderHelper.URI_LIST,\n\t\t\t\tProviderHelper.matchPath(\"/list/foo\"));\n\t\tassertEquals(ProviderHelper.URI_LIST,\n\t\t\t\tProviderHelper.matchPath(\"list/foo/bar/\"));\n\n\t\t// Details\n\t\tassertEquals(ProviderHelper.URI_DETAILS,\n\t\t\t\tProviderHelper.matchPath(\"/details/foo\"));\n\t\tassertEquals(ProviderHelper.URI_DETAILS,\n\t\t\t\tProviderHelper.matchPath(\"details/foo/bar/\"));\n\n\t\t// These uris are invalid\n\t\tassertEquals(ProviderHelper.URI_NOMATCH,\n\t\t\t\tProviderHelper.matchPath(\"details\"));\n\n\t\tassertEquals(ProviderHelper.URI_NOMATCH,\n\t\t\t\tProviderHelper.matchPath(\"details/\"));\n\n\t\tassertEquals(ProviderHelper.URI_NOMATCH,\n\t\t\t\tProviderHelper.matchPath(\"unknownpredicate/foo/bar\"));\n\t}\n\n\t@Test\n\tpublic void testJoin() throws Exception {\n\t\tassertEquals(\"/foo/bar\", ProviderHelper.join(\"/foo\", \"bar\"));\n\t\tassertEquals(\"/foo/bar\", ProviderHelper.join(\"/foo\", \"/bar\"));\n\t\tassertEquals(\"/foo/bar\", ProviderHelper.join(\"/foo/\", \"/bar\"));\n\t\tassertEquals(\"/foo/bar\", ProviderHelper.join(\"/foo/\", \"bar\"));\n\t\tassertEquals(\"/\", ProviderHelper.join(\"/\", \"/\"));\n\t\tassertEquals(\"/\", ProviderHelper.join(\"/\", \"\"));\n\t\tassertEquals(\"/\", ProviderHelper.join(\"\", \"/\"));\n\t}\n}"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/RFCDateTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport android.util.Log;\n\nimport com.nononsenseapps.helpers.RFC3339Date;\n\nimport junit.framework.TestCase;\n\nimport java.util.Calendar;\n\npublic class RFCDateTest extends TestCase {\n\n\tstatic final String TAG = \"nononsenseapps rfctest\";\n\n\t// Sun May  5 23:53:10 2013\n\t//static final long atime = 1367790790000L;\n\t// 2 Hours in milli seconds, for MY TIMEZONE\n\t//static final long twohours = 7200000L;\n\n//\tpublic void testCalendar() {\n//\t\tCalendar c = Calendar.getInstance();\n//\t\tc.setTimeInMillis(RFC3339Date.localAsRFC3339(atime));\n//\t\t\n//\t\tassertEquals(\"GMT would show as 21:53\", 21, c.get(Calendar.HOUR_OF_DAY));\n//\t\t\n//\t\t//Log.d(TAG, \"gtinm: \" + c.getTimeInMillis() + \", gtgt: \" + c.getTime().getTime());\n//\t\t//assertTrue(\"Is wrong time\", c.getTimeInMillis() == c.getTime().getTime());\n//\t}\n\n//\tpublic void testUTCFUCKSHIT() throws IndexOutOfBoundsException, ParseException {\n//\t\tLog.d(TAG, \"Start\");\n//\t\tLog.d(TAG, RFC3339Date.localAsRFC3339(atime));\n//\t\tLog.d(TAG, RFC3339Date.UTCAsRFC3339(atime));\n//\t\tString a = RFC3339Date.localAsRFC3339(atime);\n//\t\tLong l = RFC3339Date.parseRFC3339Date(a).getTime();\n//\t\tLog.d(TAG, RFC3339Date.UTCAsRFC3339(l));\n//\t\tLog.d(TAG, RFC3339Date.localAsRFC3339(l));\n//\t\tLog.d(TAG, \"End\");\n//\t\t\n//\t\t// Should return UTC time!\n//\t\tfinal long utctime = RFC3339Date.localMilliToUTCMilli(atime);\n//\t\t\n//\t\tassertEquals(\"If UTC, difference should be two hours: \" + atime + \", \" + utctime, twohours, atime - utctime);\n//\t}\n\n\tpublic void test_asRFC3339ZuluDate() {\n\t\tString result = RFC3339Date.asRFC3339ZuluDate(1402275911568L);\n\t\tassertEquals(\"Dates should be equal\", result, \"2014-06-09T00:00:00Z\");\n\t}\n\n\tpublic void testParseRFCDateBackAndForth() {\n\t\t// Make sure conversion is consistent\n\t\t// Calendar returns local time\n\t\tfinal long long1 = Calendar.getInstance().getTime().getTime();\n\t\t// String neutral\n\t\tfinal String string1 = RFC3339Date.asRFC3339(long1);\n\n\t\tLog.d(TAG, long1 + \" = \" + string1);\n\n\t\ttry {\n\t\t\t// utc\n\t\t\tfinal long long2 = RFC3339Date.parseRFC3339Date(string1).getTime();\n\t\t\tfinal String string2 = RFC3339Date.asRFC3339(long2);\n\t\t\t// utc again\n\t\t\tfinal long long3 = RFC3339Date.parseRFC3339Date(string2).getTime();\n\n\t\t\tLog.d(TAG, long2 + \" = \" + string2);\n\n\t\t\tassertEquals(\"TimeInMilli did not match\", long1 / 1000, long3 / 1000);\n\t\t\t//assertEquals(\"RFC String did not match\", string1, string2);\n\t\t} catch (Exception e) {\n\t\t\tfail(e.getLocalizedMessage());\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/StorageTest.java",
    "content": "package com.nononsenseapps.notepad.test;\n\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononsenseapps.helpers.FileHelper;\n\nimport junit.framework.TestCase;\n\nimport java.io.File;\n\n/**\n * Various tests related to storage, filesystem, ...\n */\npublic class StorageTest extends TestCase {\n\n\tpublic void testIfExternalStorageIsAvailable() {\n\t\tvar context = InstrumentationRegistry\n\t\t\t\t.getInstrumentation()\n\t\t\t\t.getTargetContext();\n\t\tFile dir = context.getExternalFilesDir(\"example\");\n\t\tassertNotNull(\"External storage is not available!\", dir);\n\t\tString dir2 = FileHelper.getUserSelectedOrgDir(context);\n\t\tassertNotNull(\"Can't determine org directory!\", dir2);\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<!-- Declare the contents of this Android application -->\n<manifest\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:installLocation=\"internalOnly\">\n\n\t<!-- TODO these permissions are useless as of now: we don't have a synchronization\n\t      method that uses these. -->\n\n\t<!-- for ContentResolver.getIsSyncable -->\n\t<uses-permission android:name=\"android.permission.READ_SYNC_SETTINGS\"/>\n\t<!-- for ContentResolver.isSyncActive -->\n\t<uses-permission android:name=\"android.permission.READ_SYNC_STATS\"/>\n\t<!-- for ContentResolver.setIsSyncable -->\n\t<uses-permission android:name=\"android.permission.WRITE_SYNC_SETTINGS\"/>\n\n\t<!-- We don't need Write permission for the filesystem:\n\t\t* /storage/emulated/0/Android/data/packagename/ is always accessible to us\n\t \t* Json backups are saved with methods that don't require write permission\n\t \tSee https://developer.android.com/training/data-storage\n\t-->\n\n\t<!-- For notifications -->\n\t<uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\"/>\n\t<uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\"/>\n\t<uses-permission android:name=\"android.permission.SCHEDULE_EXACT_ALARM\"/>\n\n\t<application\n\t\tandroid:name=\"com.nononsenseapps.notepad.NnnApp\"\n\t\tandroid:allowBackup=\"true\"\n\t\tandroid:hardwareAccelerated=\"true\"\n\t\tandroid:icon=\"@drawable/app_icon\"\n\t\tandroid:label=\"@string/nononsense_notes\"\n\t\tandroid:restoreAnyVersion=\"true\">\n\n\t\t<!-- If you need more fine-grained control on what can be backed up, visit\n\t\thttps://developer.android.com/guide/topics/data/autobackup#IncludingFiles\n\t\tbut the default behavior already backs up the database, shared preferences\n\t\tand org files to google drive, for users that choose to do it, and files\n\t\tare automatically encrypted, so there is nothing more you need to do -->\n\n\t\t<!-- Broadcast receiver for accepting actions from widget for example -->\n\t\t<receiver\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.NotePadBroadcastReceiver\"\n\t\t\tandroid:enabled=\"true\"\n\t\t\tandroid:exported=\"true\"\n\t\t\ttools:ignore=\"ExportedReceiver\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"com.nononsenseapps.SetNoteComplete\"/>\n\t\t\t\t<action android:name=\"com.nononsenseapps.SetNoteIncomplete\"/>\n\t\t\t</intent-filter>\n\t\t</receiver>\n\n\t\t<!-- Main Activity -->\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.activities.main.ActivityMain_\"\n\t\t\tandroid:exported=\"true\"\n\t\t\tandroid:label=\"@string/app_name_short\"\n\t\t\tandroid:theme=\"@style/ThemeNnnLight\"\n\t\t\tandroid:windowSoftInputMode=\"adjustResize\">\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"android.app.default_searchable\"\n\t\t\t\tandroid:value=\"com.nononsenseapps.notepad.activities.ActivitySearch\"/>\n\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.MAIN\"/>\n\n\t\t\t\t<category android:name=\"android.intent.category.LAUNCHER\"/>\n\t\t\t</intent-filter>\n\t\t\t<intent-filter\n\t\t\t\tandroid:label=\"@string/resolve_edit\"\n\t\t\t\ttools:ignore=\"AppLinkUrlError\">\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\"/>\n\t\t\t\t<action android:name=\"android.intent.action.EDIT\"/>\n\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\"/>\n\n\t\t\t\t<data android:mimeType=\"vnd.android.cursor.item/vnd.nononsenseapps.list\"/>\n\t\t\t</intent-filter>\n\t\t\t<intent-filter\n\t\t\t\tandroid:label=\"@string/resolve_edit\"\n\t\t\t\ttools:ignore=\"AppLinkUrlError\">\n\t\t\t\t<action android:name=\"android.intent.action.INSERT\"/>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\"/>\n\t\t\t\t<action android:name=\"android.intent.action.EDIT\"/>\n\t\t\t\t<action android:name=\"com.nononsenseapps.completenote\"/>\n\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\"/>\n\n\t\t\t\t<data android:mimeType=\"vnd.android.cursor.item/vnd.nononsenseapps.note\"/>\n\t\t\t</intent-filter>\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.SEND\"/>\n\t\t\t\t<!-- Voice command \"note to self\" in google search -->\n\t\t\t\t<action android:name=\"com.google.android.gm.action.AUTO_SEND\"/>\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\"/>\n\t\t\t\t<data android:mimeType=\"text/*\"/>\n\t\t\t\t<!-- <data android:mimeType=\"text/*\" android:path=\"*.txt\" />  Would like to be able to open files in future -->\n\t\t\t</intent-filter>\n\t\t</activity>\n\n\t\t<!-- Search activity -->\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.activities.ActivitySearch\"\n\t\t\tandroid:exported=\"false\"\n\t\t\tandroid:label=\"@string/search_hint\"\n\t\t\tandroid:launchMode=\"singleTop\"\n\t\t\tandroid:theme=\"@style/ThemeNnnLight\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.SEARCH\"/>\n\t\t\t</intent-filter>\n\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"android.app.searchable\"\n\t\t\t\tandroid:resource=\"@xml/searchable\"/>\n\t\t</activity>\n\n\t\t<!-- Deleted tasks activity -->\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.activities.ActivitySearchDeleted\"\n\t\t\tandroid:exported=\"true\"\n\t\t\tandroid:label=\"@string/archive\"\n\t\t\tandroid:launchMode=\"singleTop\"\n\t\t\tandroid:theme=\"@style/ThemeNnnLight\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.SEARCH\"/>\n\t\t\t</intent-filter>\n\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"android.app.searchable\"\n\t\t\t\tandroid:resource=\"@xml/searchabledeleted\"/>\n\t\t</activity>\n\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.prefs.PrefsActivity\"\n\t\t\tandroid:exported=\"true\"\n\t\t\tandroid:label=\"@string/menu_preferences\"\n\t\t\tandroid:parentActivityName=\"com.nononsenseapps.notepad.activities.main.ActivityMain_\"\n\t\t\tandroid:theme=\"@style/ThemeBaseDark\">\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"android.support.PARENT_ACTIVITY\"\n\t\t\t\tandroid:value=\"com.nononsenseapps.notepad.activities.main.ActivityMain_\"/>\n\n\t\t\t<intent-filter>\n\t\t\t\t<category android:name=\"android.intent.category.PREFERENCE\"/>\n\n\t\t\t\t<action android:name=\"android.intent.action.MAIN\"/>\n\t\t\t\t<action android:name=\"android.intent.action.EDIT\"/>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\"/>\n\t\t\t\t<action android:name=\"android.settings.SETTINGS\"/>\n\t\t\t</intent-filter>\n\t\t</activity>\n\n\t\t<!-- List Widget -->\n\t\t<receiver\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.widget.list.ListWidgetProvider\"\n\t\t\tandroid:exported=\"true\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.appwidget.action.APPWIDGET_UPDATE\"/>\n\t\t\t</intent-filter>\n\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"android.appwidget.provider\"\n\t\t\t\tandroid:resource=\"@xml/listwidgetinfo\"/>\n\t\t</receiver>\n\n\t\t<service\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.widget.list.ListWidgetService\"\n\t\t\tandroid:exported=\"false\"\n\t\t\tandroid:permission=\"android.permission.BIND_REMOTEVIEWS\"/>\n\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.widget.list.ListWidgetConfig\"\n\t\t\tandroid:exported=\"true\"\n\t\t\tandroid:theme=\"@style/ThemeWidgetConfig\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.appwidget.action.APPWIDGET_CONFIGURE\"/>\n\t\t\t</intent-filter>\n\t\t</activity>\n\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.widget.shortcut.ShortcutConfig\"\n\t\t\tandroid:exported=\"true\"\n\t\t\tandroid:label=\"@string/notes_shortcut\"\n\t\t\tandroid:theme=\"@style/ThemeNnnLight\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.CREATE_SHORTCUT\"/>\n\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\"/>\n\t\t\t</intent-filter>\n\t\t</activity>\n\n\t\t<!-- Handles notifications associated with notes -->\n\t\t<receiver\n\t\t\tandroid:name=\"com.nononsenseapps.helpers.NotificationHelper\"\n\t\t\tandroid:exported=\"true\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.BOOT_COMPLETED\"/>\n\t\t\t\t<action android:name=\"android.intent.action.RUN\"/>\n\t\t\t</intent-filter>\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.DELETE\"/>\n\t\t\t\t<action android:name=\"com.nononsenseapps.notepad.ACTION.COMPLETE\"/>\n\t\t\t\t<action android:name=\"com.nononsenseapps.notepad.ACTION.SNOOZE\"/>\n\t\t\t\t<action android:name=\"com.nononsenseapps.notepad.ACTION.RESCHEDULE\"/>\n\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\"/>\n\n\t\t\t\t<data android:mimeType=\"vnd.android.cursor.item/vnd.nononsenseapps.notification\"/>\n\t\t\t</intent-filter>\n\t\t</receiver>\n\n\t\t<!-- Dashclock -->\n\t\t<service\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.dashclock.TasksExtension\"\n\t\t\tandroid:exported=\"true\"\n\t\t\tandroid:icon=\"@drawable/ic_stat_notification_edit\"\n\t\t\tandroid:label=\"@string/dashclock_nononsense_notes\"\n\t\t\tandroid:permission=\"com.google.android.apps.dashclock.permission.READ_EXTENSION_DATA\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"com.google.android.apps.dashclock.Extension\"/>\n\t\t\t</intent-filter>\n\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"protocolVersion\"\n\t\t\t\tandroid:value=\"1\"/>\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"description\"\n\t\t\t\tandroid:value=\"Displays your tasks from NoNonsense Notes.\"/>\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"settingsActivity\"\n\t\t\t\tandroid:value=\"com.nononsenseapps.notepad.dashclock.DashclockPrefActivity\"/>\n\t\t</service>\n\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.dashclock.DashclockPrefActivity\"\n\t\t\tandroid:exported=\"true\"\n\t\t\tandroid:label=\"@string/dashclock_title_activity_tasks_settings\"\n\t\t\tandroid:permission=\"com.google.android.apps.dashclock.permission.READ_EXTENSION_DATA\"\n\t\t\tandroid:theme=\"@style/DashClockSettings.Theme\"/>\n\n\t\t<!-- DataBase provider -->\n\t\t<!-- its authority must be unique among different buildTypes (debug, fdroid, playstore)\n\t\t and equal to the one used in searchable.xml -->\n\t\t<provider\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.database.MyContentProvider\"\n\t\t\tandroid:authorities=\"@string/NnnAuthority_1\"\n\t\t\tandroid:enabled=\"true\"\n\t\t\tandroid:exported=\"false\"\n\t\t\tandroid:grantUriPermissions=\"true\"/>\n\n\t\t<!-- Time Machine -->\n\t\t<activity\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.activities.ActivityTaskHistory\"\n\t\t\tandroid:label=\"@string/timemachine\"/>\n\n\t\t<!-- Receiver that schedules background sync for File -->\n\t\t<receiver\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.sync.orgsync.BackgroundSyncScheduler\"\n\t\t\tandroid:enabled=\"true\"\n\t\t\tandroid:exported=\"true\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.BOOT_COMPLETED\"/>\n\t\t\t\t<action android:name=\"android.intent.action.RUN\"/>\n\t\t\t</intent-filter>\n\t\t</receiver>\n\n\t\t<!-- Service that syncs with files -->\n\t\t<service\n\t\t\tandroid:name=\"com.nononsenseapps.notepad.sync.orgsync.OrgSyncService\"\n\t\t\tandroid:enabled=\"true\"\n\t\t\tandroid:exported=\"false\"\n\t\t\tandroid:label=\"File Watcher\"/>\n\n\t\t<!-- A couple of providers -->\n\t\t<!-- gradle automatically replaces ${applicationId} when building -->\n\t\t<provider\n\t\t\tandroid:name=\".android.provider.TextFileProvider\"\n\t\t\tandroid:authorities=\"${applicationId}.TESTPROVIDER.AUTHORITY\"\n\t\t\tandroid:enabled=\"true\"\n\t\t\tandroid:exported=\"false\"\n\t\t\tandroid:icon=\"@drawable/ic_sd_storage_24dp\"\n\t\t\tandroid:label=\"SD-card files\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"com.nononsenseapps.notepad.PROVIDER\"/>\n\t\t\t</intent-filter>\n\t\t\t<!-- Required, only one possible value atm -->\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"protocolVersion\"\n\t\t\t\tandroid:value=\"1\"/>\n\t\t\t<!-- Optional, if absent assumed to be false -->\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"requiresConfig\"\n\t\t\t\tandroid:value=\"false\"/>\n\t\t\t<!-- Semi-Optional. If requiresConfig is true, this is mandatory. -->\n\t\t\t<!--<meta-data android:name=\"settingsActivity\"\n\t\t\t\tandroid:value=\".ExampleSettingsActivity\" />-->\n\t\t\t<!-- Optional, specifies behavior of \"+\"-button in root view.\n\t\t\t\t Possible values: containers, items, containersAndItems, nestableItems-->\n\t\t\t<!-- <meta-data android:name=\"fabBehaviorRoot\" android:value=\"containersAndItems\" /> -->\n\t\t\t<!-- Optional, same values as root, but specifies behavior anywhere.\n\t\t\t\t Root-value takes precedence at root-level, otherwise this value is used. -->\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"fabBehavior\"\n\t\t\t\tandroid:value=\"containersAndItems\"/>\n\t\t</provider>\n\n\t\t<!-- TODO useless ? -->\n\t\t<!-- ${applicationId} makes the authority unique, allowing to install the fdroid\n\t\t and play store versions simultaneously -->\n\t\t<provider\n\t\t\tandroid:name=\".android.provider.DummyProvider\"\n\t\t\tandroid:authorities=\"${applicationId}.DUMMYPROVIDER.AUTHORITY\"\n\t\t\tandroid:enabled=\"true\"\n\t\t\tandroid:exported=\"false\"\n\t\t\tandroid:icon=\"@drawable/app_icon\"\n\t\t\tandroid:label=\"Dummy items\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"com.nononsenseapps.notepad.PROVIDER\"/>\n\t\t\t</intent-filter>\n\t\t\t<!-- Required, only one possible value atm -->\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"protocolVersion\"\n\t\t\t\tandroid:value=\"1\"/>\n\t\t\t<!-- Optional, if absent assumed to be false -->\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"requiresConfig\"\n\t\t\t\tandroid:value=\"false\"/>\n\t\t\t<!-- Semi-Optional. If requiresConfig is true, this is mandatory. -->\n\t\t\t<!--<meta-data android:name=\"settingsActivity\"\n\t\t\t\tandroid:value=\".ExampleSettingsActivity\" />-->\n\t\t\t<!-- Optional, specifies behavior of \"+\"-button in root view.\n\t\t\t\t Possible values: containers, items, containersAndItems,nestableItems-->\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"fabBehaviorRoot\"\n\t\t\t\tandroid:value=\"nestableItems\"/>\n\t\t\t<!-- Optional, same values as root, but specifies behavior anywhere.\n\t\t\t\t Root-value takes precedence at root-level, otherwise this value is used. -->\n\t\t\t<meta-data\n\t\t\t\tandroid:name=\"fabBehavior\"\n\t\t\t\tandroid:value=\"nestableItems\"/>\n\t\t</provider>\n\n\t</application>\n</manifest>\n"
  },
  {
    "path": "app/src/main/assets/.gitignore",
    "content": "secretkeys.properties\n"
  },
  {
    "path": "app/src/main/java/com/google/android/apps/dashclock/ui/DragGripView.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.google.android.apps.dashclock.ui;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\n\nimport com.nononsenseapps.notepad.R;\n\npublic class DragGripView extends View {\n\tprivate static final int[] ATTRS = new int[] {\n\t\t\tandroid.R.attr.gravity,\n\t\t\tandroid.R.attr.color,\n\t};\n\n\tprivate static final int HORIZ_RIDGES = 2;\n\n\tprivate int mGravity = Gravity.END;\n\n\tprivate final Paint mRidgePaint;\n\n\tprivate final float mRidgeSize;\n\tprivate final float mRidgeGap;\n\n\tprivate int mWidth;\n\tprivate int mHeight;\n\n\tpublic DragGripView(Context context) {\n\t\tthis(context, null, 0);\n\t}\n\n\tpublic DragGripView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic DragGripView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\n\t\tfinal TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);\n\t\tmGravity = a.getInteger(0, mGravity);\n\n\t\tint mColor = a.getColor(1, /* default: */ 0x33333333);\n\t\ta.recycle();\n\n\t\tfinal Resources res = getResources();\n\t\tmRidgeSize = res.getDimensionPixelSize(R.dimen.drag_grip_ridge_size);\n\t\tmRidgeGap = res.getDimensionPixelSize(R.dimen.drag_grip_ridge_gap);\n\n\t\tmRidgePaint = new Paint();\n\t\tmRidgePaint.setColor(mColor);\n\t}\n\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\tsetMeasuredDimension(\n\t\t\t\tView.resolveSize(\n\t\t\t\t\t\t(int) (HORIZ_RIDGES * (mRidgeSize + mRidgeGap) - mRidgeGap)\n\t\t\t\t\t\t\t\t+ getPaddingLeft() + getPaddingRight(),\n\t\t\t\t\t\twidthMeasureSpec),\n\t\t\t\tView.resolveSize(\n\t\t\t\t\t\t(int) mRidgeSize,\n\t\t\t\t\t\theightMeasureSpec));\n\t}\n\n\t@Override\n\tprotected void onDraw(Canvas canvas) {\n\t\tsuper.onDraw(canvas);\n\n\t\tfloat drawWidth = HORIZ_RIDGES * (mRidgeSize + mRidgeGap) - mRidgeGap;\n\t\tfloat drawLeft;\n\n\t\t//getLayoutDirection()\n\t\tswitch (Gravity.getAbsoluteGravity(mGravity, LAYOUT_DIRECTION_LTR)\n\t\t\t\t& Gravity.HORIZONTAL_GRAVITY_MASK) {\n\t\t\tcase Gravity.CENTER_HORIZONTAL:\n\t\t\t\tdrawLeft = getPaddingLeft()\n\t\t\t\t\t\t+ ((mWidth - getPaddingLeft() - getPaddingRight()) - drawWidth) / 2;\n\t\t\t\tbreak;\n\t\t\tcase Gravity.RIGHT:\n\t\t\t\tdrawLeft = getWidth() - getPaddingRight() - drawWidth;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tdrawLeft = getPaddingLeft();\n\t\t}\n\n\t\tint vertRidges = (int) ((mHeight - getPaddingTop() - getPaddingBottom() + mRidgeGap)\n\t\t\t\t/ (mRidgeSize + mRidgeGap));\n\t\tfloat drawHeight = vertRidges * (mRidgeSize + mRidgeGap) - mRidgeGap;\n\t\tfloat drawTop = getPaddingTop()\n\t\t\t\t+ ((mHeight - getPaddingTop() - getPaddingBottom()) - drawHeight) / 2;\n\n\t\tfor (int y = 0; y < vertRidges; y++) {\n\t\t\tfor (int x = 0; x < HORIZ_RIDGES; x++) {\n\t\t\t\tcanvas.drawRect(\n\t\t\t\t\t\tdrawLeft + x * (mRidgeSize + mRidgeGap),\n\t\t\t\t\t\tdrawTop + y * (mRidgeSize + mRidgeGap),\n\t\t\t\t\t\tdrawLeft + x * (mRidgeSize + mRidgeGap) + mRidgeSize,\n\t\t\t\t\t\tdrawTop + y * (mRidgeSize + mRidgeGap) + mRidgeSize,\n\t\t\t\t\t\tmRidgePaint);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onSizeChanged(int w, int h, int oldw, int oldh) {\n\t\tsuper.onSizeChanged(w, h, oldw, oldh);\n\t\tmHeight = h;\n\t\tmWidth = w;\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/ActivityHelper.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.content.res.Configuration;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\n\nimport java.util.Arrays;\nimport java.util.Locale;\n\n/**\n * Contains helper methods for activities\n */\npublic final class ActivityHelper {\n\n\t// TODO everything in this \"helpers\" namespace could be moved to its own\n\t//  gradle module. This would speed up builds, but maybe it's harder to manage?\n\n\t// forbid instances: it's a static class\n\tprivate ActivityHelper() {}\n\n\t/**\n\t * @return the users's default or selected locale\n\t */\n\tpublic static Locale getUserLocale(Context context) {\n\t\tfinal SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n\t\tString lang = prefs.getString(context.getString(R.string.pref_locale), \"\");\n\t\tfinal Locale locale;\n\t\tif (lang.isEmpty())\n\t\t\tlocale = Locale.getDefault();\n\t\telse if (lang.length() == 5) {\n\t\t\tlocale = new Locale(lang.substring(0, 2), lang.substring(3, 5));\n\t\t} else if (lang.length() == 3) {\n\t\t\t// for example: \"vec\"\n\t\t\tlocale = new Locale(lang);\n\t\t} else {\n\t\t\tlocale = new Locale(lang.substring(0, 2));\n\t\t}\n\n\t\treturn locale;\n\t}\n\n\t/**\n\t * Set configured locale on the given activity. Call it before Activity.onCreate()\n\t */\n\tpublic static void setSelectedLanguage(@NonNull AppCompatActivity context) {\n\t\tSharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n\t\tConfiguration config = context.getResources().getConfiguration();\n\n\t\tString lang = prefs.getString(context.getString(R.string.pref_locale), \"\");\n\t\tif (lang.isEmpty()) {\n\t\t\t// usually the user doesn't use a custom language, avoid running useless code\n\t\t\treturn;\n\t\t}\n\n\t\tboolean localeExists = Arrays.asList(Locale.getISOLanguages()).contains(lang);\n\t\tif (!localeExists) {\n\t\t\tNnnLogger.warning(ActivityHelper.class,\n\t\t\t\t\t\"Trying to set a locale that does not exist on this device: \" + lang);\n\t\t}\n\t\tif (!config.locale.toString().equals(lang)) {\n\t\t\tconfig.locale = getUserLocale(context);\n\t\t\tcontext.getResources()\n\t\t\t\t\t.updateConfiguration(config, context.getResources().getDisplayMetrics());\n\t\t}\n\t\tif (context instanceof OnSharedPreferenceChangeListener) {\n\t\t\tprefs.registerOnSharedPreferenceChangeListener(\n\t\t\t\t\t(OnSharedPreferenceChangeListener) context);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/DocumentFileHelper.java",
    "content": "package com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.net.Uri;\nimport android.os.ParcelFileDescriptor;\nimport android.provider.MediaStore;\nimport android.webkit.MimeTypeMap;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.provider.DocumentsContractCompat;\nimport androidx.documentfile.provider.DocumentFile;\n\nimport com.nononsenseapps.notepad.prefs.BackupPrefs;\n\nimport java.io.FileDescriptor;\nimport java.io.FileOutputStream;\nimport java.util.function.Function;\n\n/**\n * Functions to work with {@link DocumentFile}. See\n * <a href=\"https://developer.android.com/training/data-storage/shared/documents-files#create-file\">here</a>\n * And, to understand why {@link DocumentFile} is better than {@link MediaStore}, see\n * <a href=\"https://developer.android.com/training/data-storage\">here</a>.\n * This API can read files created by this app even after you reinstall it, so it's\n * better than {@link MediaStore}\n */\npublic final class DocumentFileHelper {\n\n\t/**\n\t * Hardcoded filename of the backup file. The user chooses where to save this\n\t */\n\tprivate static final String backupJsonFileName = \"NoNonsenseNotes_Backup.json\";\n\n\tpublic static boolean isWritableFolder(DocumentFile docDir) {\n\t\treturn docDir != null && docDir.exists() && docDir.isDirectory() && docDir.canWrite();\n\t}\n\n\t/**\n\t * Get a {@link FileDescriptor} for the file at the given {@link Uri} and\n\t * run the code in the {@link Function}\n\t *\n\t * @return TRUE if it finished, FALSE if there was an error\n\t */\n\tprivate static boolean doWithFileDescriptorFor(@NonNull DocumentFile target,\n\t\t\t\t\t\t\t\t\t\t\t\t   @NonNull Context context,\n\t\t\t\t\t\t\t\t\t\t\t\t   Function<FileDescriptor, Boolean> function) {\n\t\ttry {\n\t\t\tParcelFileDescriptor pfd = context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.openFileDescriptor(target.getUri(), \"rw\");\n\t\t\tFileDescriptor fileDescriptor = pfd.getFileDescriptor();\n\n\t\t\tboolean ok = fileDescriptor.valid();\n\t\t\tif (!ok) return false;\n\n\t\t\tfunction.apply(fileDescriptor);\n\t\t\tpfd.close();\n\t\t\treturn true;\n\n\t\t} catch (Exception ex) {\n\t\t\tNnnLogger.exception(ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Write \"content\" in \"destination\" using the {@link DocumentFile} API\n\t *\n\t * @param destination a file, not a folder\n\t * @return TRUE if it succeeded, FALSE otherwise\n\t */\n\tpublic static boolean write(String content, DocumentFile destination, Context context) {\n\t\tif (content == null || destination == null || context == null) return false;\n\t\tif (!DocumentsContractCompat.isDocumentUri(context, destination.getUri())) return false;\n\t\treturn doWithFileDescriptorFor(destination, context, fd -> {\n\t\t\ttry {\n\t\t\t\tvar fileOutputStream = new FileOutputStream(fd);\n\t\t\t\tfileOutputStream.write(content.getBytes());\n\t\t\t\t// Let the document provider know you're done by closing the stream.\n\t\t\t\tfileOutputStream.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t}\n\n\t/**\n\t * Delete the existing Json file and create a new one, for the backup\n\t *\n\t * @return the newly created {@link DocumentFile}, or null if it wasn't possible to create one\n\t */\n\tpublic static DocumentFile createBackupJsonFile(Context context) {\n\t\tUri dirUri = BackupPrefs.getSelectedBackupDirUri(context);\n\t\tif (dirUri == null) return null;\n\n\t\tvar docDir = DocumentFile.fromTreeUri(context, dirUri);\n\t\tif (docDir == null) return null;\n\t\tvar oldDocFile = docDir.findFile(backupJsonFileName);\n\t\tif (oldDocFile != null && oldDocFile.exists()) {\n\t\t\t// already exists => delete it before creating a new one\n\t\t\toldDocFile.delete();\n\t\t}\n\n\t\t// android doesn't care about the mimetype anyway, having the extension\n\t\t// in displayName is enough\n\t\tString mt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(\"json\");\n\t\treturn docDir.createFile(mt, backupJsonFileName);\n\t}\n\n\t/**\n\t * @return the {@link DocumentFile} representing the json file that will be read to restore\n\t * the backup, or NULL if it could not find the file. It's in the user-selected backup\n\t * directory. See {@link BackupPrefs}\n\t */\n\t@Nullable\n\tpublic static DocumentFile getSelectedBackupJsonFile(Context context) {\n\t\tUri dirUri = BackupPrefs.getSelectedBackupDirUri(context);\n\t\t// user didn't choose a folder\n\t\tif (dirUri == null) return null;\n\t\t// somehow it's invalid\n\t\tif (!DocumentsContractCompat.isTreeUri(dirUri)) return null;\n\n\t\tDocumentFile dirDoc = DocumentFile.fromTreeUri(context, dirUri);\n\t\tif (dirDoc == null) return null;\n\n\t\tDocumentFile fileDoc = dirDoc.findFile(\"NoNonsenseNotes_Backup.json\");\n\t\tif (fileDoc != null && DocumentsContractCompat.isDocumentUri(context, fileDoc.getUri()))\n\t\t\treturn fileDoc;\n\t\telse\n\t\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/FileHelper.java",
    "content": "package com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.media.MediaScannerConnection;\nimport android.os.SystemClock;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.PrintStream;\n\n/**\n * Methods to help navigate through Google's mess regarding file access.\n * These use the {@link File} API, so they work well only in\n * {@link Context#getExternalFilesDir}. Avoid these for Android 10 and higher,\n * prefer {@link FilePickerHelper} instead\n */\npublic final class FileHelper {\n\n\t/**\n\t * Writes the given {@link String} to the given {@link File}. Does not work outside\n\t * of the external files directory, due to bad design by Google\n\t *\n\t * @return TRUE if it worked, FALSE otherwise\n\t */\n\t@Deprecated\n\tprivate static boolean writeStringToFile(String content, File target) {\n\t\tif (content == null || target == null) return false;\n\t\tif (target.isDirectory() || target.getParentFile() == null) return false;\n\n\t\tNnnLogger.debug(FileHelper.class,\n\t\t\t\t\"Writing, with PrintStream, to file \" + target.getAbsolutePath());\n\t\ttry {\n\t\t\ttarget.getParentFile().mkdirs();\n\t\t} catch (SecurityException se) {\n\t\t\tNnnLogger.error(FileHelper.class, \"Can't create: \" + target.getParentFile());\n\t\t\tNnnLogger.exception(se);\n\t\t\treturn false;\n\t\t}\n\n\t\ttry (PrintStream out = new PrintStream(new FileOutputStream(target))) {\n\t\t\tout.print(content);\n\t\t\tout.close();\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.exception(e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @return the path of the directory where ORG files are saved,\n\t * or NULL if it could not get one. This path is now hardcoded\n\t * to something like Android/data/packagename\n\t */\n\tpublic static String getUserSelectedOrgDir(@NonNull Context ctx) {\n\t\t// we are going to use the default directory:\n\t\t// /storage/emulated/0/Android/data/packagename/files/orgfiles/\n\t\tFile dir = ctx.getExternalFilesDir(\"orgfiles\");\n\n\t\t// most likely, the shared storage is not available in this device/emulator\n\t\tif (dir == null) return null;\n\n\t\t// must ensure that it exists\n\t\tif (!dir.exists()) dir.mkdirs();\n\n\t\tboolean ok = dir.exists() && dir.isDirectory() && dir.canWrite();\n\t\tif (ok) return dir.getAbsolutePath();\n\t\telse return null;\n\t}\n\n\t/**\n\t * When you delete a file in android, additional attention is required.\n\t * This function takes care of that. Does not work above API 29\n\t *\n\t * @return TRUE if it succeeded, FALSE otherwise\n\t */\n\tpublic static boolean tryDeleteFile(@NonNull File toDelete, @NonNull Context context) {\n\t\tboolean contains = toDelete\n\t\t\t\t.getAbsolutePath()\n\t\t\t\t.contains(getUserSelectedOrgDir(context));\n\t\t// the File API only works in that directory\n\t\tif (!contains) return false;\n\n\t\tif (toDelete.exists()) {\n\t\t\ttry {\n\t\t\t\tif (!toDelete.delete()) return false;\n\t\t\t} catch (SecurityException e) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// once you successfully deleted it, you have to update the media scanner to\n\t\t// let android know that the file was deleted, ELSE IT WILL CRASH!\n\t\tMediaScannerConnection.scanFile(context, new String[] { toDelete.getAbsolutePath() },\n\t\t\t\tnull, null);\n\n\t\t// wait a bit for the mediascanner to do its work\n\t\t// 2 seconds should be enough\n\t\tSystemClock.sleep(1900);\n\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/FilePickerHelper.java",
    "content": "package com.nononsenseapps.helpers;\n\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.provider.DocumentsContract;\nimport android.widget.Toast;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.provider.DocumentsContractCompat;\nimport androidx.documentfile.provider.DocumentFile;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\n\n/**\n * Methods to use android's built-in file picker. See {@link DocumentFileHelper}\n * that you can use to handle the {@link Uri} returned by this file picker\n */\npublic final class FilePickerHelper {\n\n\t/**\n\t * For onActivityResult\n\t */\n\tpublic static final int REQ_CODE = 123321;\n\n\t/**\n\t * Shows the system's default filepicker, to  let the user choose a directory. See:\n\t * <a href=\"https://developer.android.com/training/data-storage/shared/documents-files#grant-access-directory\">this link</a>\n\t *\n\t * @param prefFragComp The settings page that launched this file picker\n\t * @param initialDir   the starting directory to show, or NULL if you don't care\n\t */\n\tpublic static void showFolderPickerActivity(PreferenceFragmentCompat prefFragComp,\n\t\t\t\t\t\t\t\t\t\t\t\t@Nullable Uri initialDir) {\n\t\tvar i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);\n\n\t\t// don't add this: it stops working on some devices, like the emulator with API 25!\n\t\t// i.setType(DocumentsContract.Document.MIME_TYPE_DIR);\n\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n\t\t\t// get the previously selected Uri, if available\n\t\t\tboolean uriIsOk = initialDir != null && DocumentsContractCompat.isTreeUri(initialDir);\n\t\t\tif (uriIsOk) i.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialDir);\n\t\t\t// else the filepicker will just open in its default state. whatever.\n\t\t}\n\t\ttry {\n\t\t\t// Start the built-in filepicker\n\t\t\tprefFragComp.startActivityForResult(i, REQ_CODE);\n\t\t} catch (ActivityNotFoundException e) {\n\t\t\tToast.makeText(prefFragComp.getContext(), R.string.file_picker_not_available,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t}\n\t}\n\n\t/**\n\t * Called when the user picks a \"directory\" with the system's filepicker\n\t *\n\t * @param fromActivityResult an {@link Intent} from onActivityResult\n\t * @param keyOfPrefToUpdate  key of the preference where \"uri\" will be saved in\n\t */\n\tpublic static void onUriPicked(Intent fromActivityResult, Context context,\n\t\t\t\t\t\t\t\t   String keyOfPrefToUpdate) {\n\t\tUri uri = fromActivityResult.getData();\n\t\tif (!DocumentsContractCompat.isTreeUri(uri)) return;\n\n\t\t// represents the directory that the user just picked\n\t\t// Use this instead of the \"File\" class\n\t\tDocumentFile docDir = DocumentFile.fromTreeUri(context, uri);\n\n\t\t// to maintain permission when the device restarts\n\t\ttry {\n\t\t\tcontext.getContentResolver().takePersistableUriPermission(uri,\n\t\t\t\t\tIntent.FLAG_GRANT_READ_URI_PERMISSION\n\t\t\t\t\t\t\t| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n\t\t} catch (SecurityException se) {\n\t\t\t// no permissions found for this URI. isWritableFolder() will return false\n\t\t\tNnnLogger.warning(FilePickerHelper.class,\n\t\t\t\t\t\"Can't take persistable uri permissions from: \" + uri);\n\t\t\tNnnLogger.exception(se);\n\t\t}\n\n\t\tif (DocumentFileHelper.isWritableFolder(docDir)) {\n\t\t\t// save the uri in the preferences, with the given key\n\t\t\tPreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(context)\n\t\t\t\t\t.edit()\n\t\t\t\t\t.putString(keyOfPrefToUpdate, uri.toString())\n\t\t\t\t\t.apply();\n\t\t} else {\n\t\t\tToast.makeText(context, R.string.cannot_write_to_directory, Toast.LENGTH_SHORT).show();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/ListHelper.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.database.Cursor;\n\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.fragments.TaskListFragment;\nimport com.nononsenseapps.notepad.fragments.TaskListViewPagerFragment;\n\n/**\n * Simple utility class to hold some general functions for lists\n */\npublic final class ListHelper {\n\n\t/**\n\t * If temp list is > 0, returns it if it exists. Else, checks if a default list is set\n\t * then returns that. If none set, then returns first (alphabetical) list\n\t * Returns #{TaskListFragment.LIST_ID_ALL} if no lists in database.\n\t */\n\tpublic static long getAViewList(final Context context, final long tempList) {\n\t\tlong returnList = tempList;\n\n\t\t// TODO useless, you already have getAShowList() in this class\n\n\t\tif (returnList == TaskListFragment.LIST_ID_ALL) {\n\t\t\t// This is fine\n\t\t\treturn returnList;\n\t\t}\n\t\t// Otherwise, try and get a real list\n\t\treturnList = getARealList(context, returnList);\n\n\t\tif (returnList < 1) {\n\t\t\t// If nothing was found, return all of them in this case\n\t\t\treturnList = TaskListFragment.LIST_ID_ALL;\n\t\t}\n\n\n\t\treturn returnList;\n\t}\n\n\t/**\n\t * Guarantees default list is valid.\n\t *\n\t * @return If \"tempList\" > 0, returns it if it exists.\n\t * Else, checks if a default list is set then returns that.\n\t * If none set, then returns first (alphabetical) list.\n\t * If no lists exist in the database, returns -1.\n\t */\n\tpublic static long getARealList(final Context context, final long tempList) {\n\t\tlong returnList = tempList;\n\n\t\tif (returnList < 1 && returnList != TaskListFragment.LIST_ID_ALL) {\n\t\t\tassert returnList == -1;  // ... I think ??\n\n\t\t\t// Then check if a default list is specified\n\t\t\tSharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n\t\t\tString defListPrefName = context.getString(R.string.pref_defaultlist);\n\t\t\treturnList = Long.parseLong(prefs.getString(defListPrefName, \"-1\"));\n\t\t}\n\n\t\tif (returnList > 0) {\n\t\t\t// See if it exists\n\t\t\tfinal Cursor c = context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.query(TaskList.URI,\n\t\t\t\t\t\t\tTaskList.Columns.FIELDS,\n\t\t\t\t\t\t\tTaskList.Columns._ID + \" IS ?\",\n\t\t\t\t\t\t\tnew String[] { Long.toString(returnList) },\n\t\t\t\t\t\t\tnull);\n\t\t\tif (c != null) {\n\t\t\t\tif (c.moveToFirst()) {\n\t\t\t\t\treturnList = c.getLong(0);\n\t\t\t\t} else {\n\t\t\t\t\treturnList = -1;\n\t\t\t\t}\n\t\t\t\tc.close();\n\t\t\t}\n\t\t}\n\n\t\tif (returnList < 1) {\n\t\t\tassert returnList == -1; // ... I think ??\n\n\t\t\t// Fetch a valid list from database if previous attempts are invalid\n\t\t\tString orderingSql = context\n\t\t\t\t\t.getResources()\n\t\t\t\t\t.getString(R.string.const_as_alphabetic, TaskList.Columns.TITLE);\n\t\t\tfinal Cursor c = context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.query(TaskList.URI, TaskList.Columns.FIELDS, null,\n\t\t\t\t\t\t\tnull, orderingSql);\n\t\t\tif (c != null) {\n\t\t\t\tif (c.moveToFirst()) {\n\t\t\t\t\treturnList = c.getLong(0);\n\t\t\t\t}\n\t\t\t\tc.close();\n\t\t\t}\n\t\t}\n\n\t\treturn returnList;\n\t}\n\n\t/**\n\t * For {@link TaskListViewPagerFragment}\n\t *\n\t * @param tempList from ActivityMainHelper.getListId()\n\t * @return and ID that might belong to a \"meta list\"\n\t */\n\tpublic static long getAShowList(final Context context, final long tempList) {\n\t\tlong returnList = tempList;\n\n\t\tif (returnList == -1) {\n\t\t\t// Then check if a default list is specified\n\t\t\tSharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n\t\t\tString defListId = prefs.getString(\n\t\t\t\t\tcontext.getString(R.string.pref_defaultlist), \"-1\");\n\t\t\treturnList = prefs.getLong(\n\t\t\t\t\tcontext.getString(R.string.pref_defaultstartlist), Long.parseLong(defListId));\n\t\t}\n\n\t\tif (returnList == -1) {\n\t\t\treturnList = getARealList(context, returnList);\n\t\t}\n\n\t\t// If nothing was found, show ALL\n\t\tif (returnList == -1) {\n\t\t\treturnList = TaskListFragment.LIST_ID_ALL;\n\t\t}\n\n\t\treturn returnList;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/NnnLogger.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\n/**\n * Our own No Nonsense Notes Logger\n */\npublic final class NnnLogger {\n\n\t/**\n\t * Logs the given exception with tag \"NNN\"\n\t */\n\tpublic static void exception(@NonNull Exception e) {\n\t\tString msg = e.getMessage();\n\t\tLog.e(\"NNN\", msg == null ? \"(No message)\" : msg);\n\n\t\tString stackTrace = Log.getStackTraceString(e);\n\t\tLog.e(\"NNN\", stackTrace);\n\t}\n\n\t/**\n\t * Logs the given error message with tag \"NNN\". If you have an {@link Exception} object,\n\t * please use {@link NnnLogger#exception(Exception)} instead\n\t *\n\t * @param caller  the class who's calling this function. Its name is added to the message\n\t * @param message the additional message sent to logcat\n\t */\n\tpublic static <T> void error(@NonNull Class<T> caller, @NonNull String message) {\n\t\ttry {\n\t\t\tString tag2 = caller.getSimpleName();\n\t\t\tLog.e(\"NNN\", tag2 + \": \" + message);\n\t\t} catch (Exception ignored) {\n\t\t\tLog.e(\"NNN\", message);\n\t\t}\n\t}\n\n\t/**\n\t * Logs the given warning message with tag \"NNN\".\n\t *\n\t * @param caller  the class who's calling this function. Its name is added to the message\n\t * @param message the additional message sent to logcat. Mostly {@link String}, but it\n\t *                will try to write everything\n\t */\n\tpublic static <T> void warning(@NonNull Class<T> caller, @NonNull Object message) {\n\t\ttry {\n\t\t\tString tag2 = caller.getSimpleName();\n\t\t\tLog.w(\"NNN\", tag2 + \": \" + message);\n\t\t} catch (Exception ex) {\n\t\t\tLog.d(\"NNN\", \"Can't write LOG line: \" + ex.getMessage());\n\t\t}\n\t}\n\n\t/**\n\t * Logs the given message with tag \"NNN\", but only in debug mode\n\t *\n\t * @param caller  the class who's calling this function. Its name is added to the message\n\t * @param message the additional message sent to logcat. Mostly {@link String}, but it\n\t *                will try to write everything\n\t */\n\tpublic static <T> void debug(@NonNull Class<T> caller, @NonNull Object message) {\n\t\ttry {\n\t\t\tString tag2 = caller.getSimpleName();\n\t\t\tLog.d(\"NNN\", tag2 + \": \" + message);\n\t\t} catch (Exception ex) {\n\t\t\tLog.d(\"NNN\", \"Can't write LOG line: \" + ex.getMessage());\n\t\t}\n\t}\n\n\t// TODO this file is in com.nononsenseapps.helpers => move it to its own gradle module.\n\t//  Having many of these (one per namespace makes sense) will speed up parallel builds.\n\t//  you can have at least 3 gradle modules: drag-sort-listview,\n\t//  nononsenseapps-helpers, nononsenseapps-utils, nononsenseapps-ui\n\t//  and stuff in com.nononsenseapps.notepad remains in the \"app\" project\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/NotificationHelper.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.annotation.TargetApi;\nimport android.app.AlarmManager;\nimport android.app.Notification;\nimport android.app.NotificationChannel;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.database.ContentObserver;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.SystemClock;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresApi;\nimport androidx.core.app.NotificationCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\n\n/**\n * Receives signals and contains helper methods to show Android Notifications. Remember that a\n * {@link android.app.Notification} is different from one of our reminders, which the database\n * saves as {@link com.nononsenseapps.notepad.database.Notification}\n */\npublic final class NotificationHelper extends BroadcastReceiver {\n\n\t// Intent notification argument\n\tpublic static final String NOTIFICATION_CANCEL_ARG = \"notification_cancel_arg\";\n\tpublic static final String NOTIFICATION_DELETE_ARG = \"notification_delete_arg\";\n\n\tstatic final String ARG_TASKID = \"taskid\";\n\tpublic static final String CHANNEL_ID = \"remindersNotificationChannelId\";\n\n\t// if you edit these, update AndroidManifest.xml !\n\tprivate static final String ACTION_COMPLETE = \"com.nononsenseapps.notepad.ACTION.COMPLETE\";\n\tprivate static final String ACTION_SNOOZE = \"com.nononsenseapps.notepad.ACTION.SNOOZE\";\n\tprivate static final String ACTION_RESCHEDULE = \"com.nononsenseapps.notepad.ACTION.RESCHEDULE\";\n\n\t/**\n\t * Fires notifications that have elapsed and sets an alarm to be woken at\n\t * the next notification.\n\t * If the intent action is ACTION_DELETE, will delete the notification with\n\t * the indicated ID, and cancel it from any active notifications.\n\t */\n\t@Override\n\tpublic void onReceive(Context context, @NonNull Intent intent) {\n\t\tString action = intent.getAction();\n\t\tif (action != null) {\n\t\t\t// should we cancel something? we decide it here:\n\n\t\t\tif (Intent.ACTION_BOOT_COMPLETED.equals(action) || Intent.ACTION_RUN.equals(action)) {\n\t\t\t\t// => can't cancel anything. Just schedule and notify at end of the function.\n\t\t\t\t// You receive:\n\t\t\t\t// Intent.ACTION_BOOT_COMPLETED when the phone is rebooted.\n\t\t\t\t// Intent.ACTION_RUN for notifications scheduled through the Alarm Manager\n\t\t\t} else {\n\t\t\t\t// something like content://com.nononsenseapps.NotePad/notification/1\n\t\t\t\tUri notifUri = intent.getData();\n\t\t\t\t// always dismiss the android notification\n\t\t\t\tcancelNotification(context, notifUri);\n\n\t\t\t\tswitch (action) {\n\t\t\t\t\tcase Intent.ACTION_DELETE, ACTION_RESCHEDULE ->\n\t\t\t\t\t\t// User swiped notification away: delete reminder\n\t\t\t\t\t\t\tcom.nononsenseapps.notepad.database.Notification\n\t\t\t\t\t\t\t\t\t.deleteOrReschedule(context, notifUri);\n\t\t\t\t\tcase ACTION_SNOOZE -> {\n\t\t\t\t\t\t// the user choose to dismiss this notification and display it later.\n\n\t\t\t\t\t\t// the one that launched the notification snoozed by the user\n\t\t\t\t\t\tvar oldReminder = com.nononsenseapps.notepad.database.Notification\n\t\t\t\t\t\t\t\t.fromUri(notifUri, context);\n\t\t\t\t\t\tif (oldReminder.isRepeating()) {\n\t\t\t\t\t\t\t// for repeating reminders\n\n\t\t\t\t\t\t\t// add a new (non-repeating) reminder, for \"snooze\" effect\n\t\t\t\t\t\t\tvar newReminder = new com.nononsenseapps.notepad.database\n\t\t\t\t\t\t\t\t\t.Notification(oldReminder.taskID);\n\t\t\t\t\t\t\tnewReminder.time = getSnoozedReminderNewTimeMillis();\n\t\t\t\t\t\t\tnewReminder.repeats = 0; // I WANT this\n\t\t\t\t\t\t\tnewReminder.save(context);\n\n\t\t\t\t\t\t\t// then, reschedule repeating reminder to next applicable day, as if\n\t\t\t\t\t\t\t// user swiped notification away\n\t\t\t\t\t\t\tcom.nononsenseapps.notepad.database.Notification\n\t\t\t\t\t\t\t\t\t.deleteOrReschedule(context, notifUri);\n\t\t\t\t\t\t\t// Later, \"newReminder\" will show up, but we handle that in other\n\t\t\t\t\t\t\t// branch of this if block.\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// for non-repeating reminders: snoozing overwrites the due time of the\n\t\t\t\t\t\t\t// reminder that triggered this notification\n\t\t\t\t\t\t\tcom.nononsenseapps.notepad.database.Notification\n\t\t\t\t\t\t\t\t\t.setTime(context, notifUri, getSnoozedReminderNewTimeMillis());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase ACTION_COMPLETE -> {\n\t\t\t\t\t\tfinal long taskId = intent.getLongExtra(ARG_TASKID, -1);\n\t\t\t\t\t\t// Complete note\n\t\t\t\t\t\tTask.setCompletedSynced(context, true, taskId);\n\t\t\t\t\t\t// Delete notifications with the same task id\n\t\t\t\t\t\tcom.nononsenseapps.notepad.database.Notification\n\t\t\t\t\t\t\t\t.removeWithTaskIdsSynced(context, taskId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// run this in ANY case\n\t\tschedule(context);\n\t}\n\n\t/**\n\t * @return the time, in unix milliseconds, that corresponds to 30 minutes from when this\n\t * function is called. Used when the user presses \"snooze\" in the notification.\n\t */\n\tpublic static long getSnoozedReminderNewTimeMillis() {\n\t\t// TODO snooze logic is hardcoded to 30' here.\n\t\t//  Set a custom timer in the preferences and load the number here\n\t\tfinal long minutes = 30;\n\n\t\t// msec/sec * sec/min * (snooze minutes)\n\t\tfinal long snoozeDelayInMillis = 1000 * 60 * minutes;\n\n\t\tfinal Calendar now = Calendar.getInstance();\n\n\t\t// it's 30 minutes from [when the user presses \"snooze\"], expressed in unix millseconds\n\t\treturn now.getTimeInMillis() + snoozeDelayInMillis;\n\t}\n\n\t/**\n\t * creates the notification channel needed on API 26 and higher to show notifications.\n\t * This is safe to call multiple times. All of its settings overwrite those of the\n\t * single notification!\n\t */\n\t@TargetApi(Build.VERSION_CODES.O)\n\t@RequiresApi(Build.VERSION_CODES.O)\n\tpublic static void createNotificationChannel(final Context context, NotificationManager nm) {\n\t\tString name = context.getString(R.string.notification_channel_name);\n\t\tString description = context.getString(R.string.notification_channel_description);\n\n\t\t// This is equivalent to notifications before API 24\n\t\t// the user can change it in the system's settings page\n\t\tint importance = NotificationManager.IMPORTANCE_DEFAULT;\n\n\t\tNotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);\n\t\tchannel.setDescription(description);\n\n\t\t// here you could also set other stuff, but the user can set all of those in the system's\n\t\t// notification channel pref. page, which can be opened through our NotificationPrefs\n\t\t// fragment. And that's better than us rewriting android code!\n\t\tnm.createNotificationChannel(channel);\n\t}\n\n\tpublic static void clearNotification(@NonNull final Context context,\n\t\t\t\t\t\t\t\t\t\t @NonNull final Intent intent) {\n\t\tif (intent.getLongExtra(NOTIFICATION_DELETE_ARG, -1) > 0) {\n\t\t\tcom.nononsenseapps.notepad.database.Notification.deleteOrReschedule(context,\n\t\t\t\t\tcom.nononsenseapps.notepad.database.Notification.getUri(\n\t\t\t\t\t\t\tintent.getLongExtra(NOTIFICATION_DELETE_ARG, -1)));\n\t\t}\n\t\tif (intent.getLongExtra(NOTIFICATION_CANCEL_ARG, -1) > 0) {\n\t\t\tNotificationHelper.cancelNotification(context,\n\t\t\t\t\t(int) intent.getLongExtra(NOTIFICATION_CANCEL_ARG, -1));\n\t\t}\n\t}\n\n\t/**\n\t * Displays notifications that have a time occurring in the past. If no notifications\n\t * like that exist, it will cancel any notifications showing.\n\t */\n\tprivate static void notifyPast(Context context) {\n\t\t// Get list of past notifications\n\t\tfinal Calendar now = Calendar.getInstance();\n\n\t\tfinal List<com.nononsenseapps.notepad.database.Notification> notifications\n\t\t\t\t= com.nononsenseapps.notepad.database.Notification\n\t\t\t\t.getNotificationsWithTime(context, now.getTimeInMillis(), true);\n\n\t\t// Remove duplicates\n\t\tmakeUnique(context, notifications);\n\n\t\tfinal NotificationManager notificationManager = (NotificationManager) context\n\t\t\t\t.getSystemService(Context.NOTIFICATION_SERVICE);\n\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n\t\t\tcreateNotificationChannel(context, notificationManager);\n\t\t}\n\n\t\tNnnLogger.debug(NotificationHelper.class,\n\t\t\t\t\"N° of notifications: \" + notifications.size());\n\n\t\t// If empty, cancel\n\t\tif (notifications.isEmpty()) {\n\t\t\t// TODO cancelAll permanent notifications here if/when that is implemented.\n\t\t\t// Don't touch others. Dont do this, it clears location\n\t\t\t// notificationManager.cancelAll();\n\t\t\treturn;\n\t\t}\n\n\t\t// else, notify\n\t\tif (!areNotificationsVisible(notificationManager)) {\n\t\t\t// android API >= 33 lets users disable notifications.\n\t\t\t// The user turned OFF notifications for this app => send a warning\n\t\t\tNnnLogger.warning(NotificationHelper.class,\n\t\t\t\t\t\"areNotificationsVisible() claims the user denied notifications\");\n\t\t\tToast.makeText(context, R.string.msg_enable_notifications,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t}\n\n\t\t// Fetch sound and vibrate settings. The following settings are ARE ONLY VALID\n\t\t// ON ANDROID API < 26, by design. Newer android versions set these things on the\n\t\t// notification CHANNEL. For that, we just bring the user to the OS settings page,\n\t\t// instead of creating our preferences code\n\t\t// => the code here is only for API 23, 24 and 25 devices\n\t\tfinal SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n\n\t\t// Always use default lights\n\t\tint lightAndVibrate = Notification.DEFAULT_LIGHTS;\n\t\t// If vibrate on, use default vibration pattern also\n\t\tif (prefs.getBoolean(context.getString(R.string.key_pref_vibrate), false))\n\t\t\tlightAndVibrate |= Notification.DEFAULT_VIBRATE;\n\n\t\t// Need to get a new one because the action buttons will duplicate otherwise\n\t\tNotificationCompat.Builder builder;\n\n\t\t// (Here there was code to group notifications together by list, but i removed it.\n\t\t// Check git history if you're interested)\n\n\t\t// get priority and ringtone. See NotificationPrefs.java\n\t\tfinal int priority = Integer.parseInt(\n\t\t\t\tprefs.getString(context.getString(R.string.key_pref_prio), \"0\"));\n\t\tfinal Uri ringtone = Uri.parse(prefs.getString(\n\t\t\t\tcontext.getString(R.string.key_pref_ringtone),\n\t\t\t\t\"DEFAULT_NOTIFICATION_URI\"));\n\n\t\t// Notify for each individually\n\t\tfor (com.nononsenseapps.notepad.database.Notification note : notifications) {\n\t\t\t// notifications.length is ~3 => optimization is not needed in this loop\n\t\t\tbuilder = getNotificationBuilder(context, priority, lightAndVibrate, ringtone);\n\t\t\tnotifyBigText(context, notificationManager, builder, note);\n\t\t}\n\n\t}\n\n\t/**\n\t * Returns a notification builder set with non-item specific properties.\n\t */\n\tprivate static NotificationCompat.Builder getNotificationBuilder(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t final int priority,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t final int lightAndVibrate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t final Uri ringtone) {\n\t\t// useless ? the small icon should be enough\n\t\tfinal Bitmap largeIcon = BitmapFactory\n\t\t\t\t.decodeResource(context.getResources(), R.drawable.app_icon);\n\n\t\t// note that many of these settings (ringtone, vibration, ...) are IGNORED in\n\t\t// android API >= 26. Instead, the user should edit the notification channel\n\t\t// preferences in the page we link to from NotificationPrefs.java\n\t\treturn new NotificationCompat\n\t\t\t\t.Builder(context, CHANNEL_ID) // we use only 1 channel in this app\n\t\t\t\t.setWhen(0)\n\t\t\t\t.setSmallIcon(R.drawable.ic_stat_notification_edit)\n\t\t\t\t.setLargeIcon(largeIcon)\n\t\t\t\t.setPriority(priority)\n\t\t\t\t.setDefaults(lightAndVibrate)\n\t\t\t\t.setAutoCancel(true)\n\t\t\t\t.setSound(ringtone)\n\t\t\t\t.setOnlyAlertOnce(true);\n\t}\n\n\t/**\n\t * Remove from the database, and the specified list, duplicate\n\t * notifications. The result is that each note is only associated with ONE\n\t * EXPIRED notification.\n\t */\n\tprivate static void makeUnique(final Context context,\n\t\t\t\t\t\t\t\t   final List<com.nononsenseapps.notepad.database.Notification> notifications) {\n\t\t// get duplicates and iterate over them\n\t\tfor (var noti : getLatestOccurence(notifications)) {\n\t\t\t// remove all but the first one from database, and big list\n\t\t\tfor (var dupNoti : getDuplicates(noti, notifications)) {\n\t\t\t\tnotifications.remove(dupNoti);\n\t\t\t\tcancelNotification(context, dupNoti);\n\t\t\t\t// Cancelled called in delete\n\t\t\t\tdupNoti.deleteOrReschedule(context);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Returns the first occurrence of each note's notification. Effectively the\n\t * returned list has unique elements with regard to the note id.\n\t */\n\tprivate static List<com.nononsenseapps.notepad.database.Notification> getLatestOccurence(\n\t\t\tfinal List<com.nononsenseapps.notepad.database.Notification> notifications) {\n\t\tfinal ArrayList<Long> seenIds = new ArrayList<>();\n\t\tfinal ArrayList<com.nononsenseapps.notepad.database.Notification> firsts = new ArrayList<>();\n\n\t\tcom.nononsenseapps.notepad.database.Notification noti;\n\t\tfor (int i = notifications.size() - 1; i >= 0; i--) {\n\t\t\tnoti = notifications.get(i);\n\t\t\tif (!seenIds.contains(noti.taskID)) {\n\t\t\t\tseenIds.add(noti.taskID);\n\t\t\t\tfirsts.add(noti);\n\t\t\t}\n\t\t}\n\t\treturn firsts;\n\t}\n\n\tprivate static List<com.nononsenseapps.notepad.database.Notification> getDuplicates(\n\t\t\tfinal com.nononsenseapps.notepad.database.Notification first,\n\t\t\tfinal List<com.nononsenseapps.notepad.database.Notification> notifications) {\n\t\tfinal ArrayList<com.nononsenseapps.notepad.database.Notification> dups = new ArrayList<>();\n\n\t\tfor (com.nononsenseapps.notepad.database.Notification noti : notifications) {\n\t\t\tif (noti.taskID.equals(first.taskID) && noti._id != first._id) {\n\t\t\t\tdups.add(noti);\n\t\t\t}\n\t\t}\n\t\treturn dups;\n\t}\n\n\t/**\n\t * Configures and shows an android notification for the given reminder.\n\t * Needs the builder that contains non-note specific values.\n\t *\n\t * @param note the reminder that triggered this android notification\n\t */\n\tprivate static void notifyBigText(final Context context,\n\t\t\t\t\t\t\t\t\t  final NotificationManager notificationManager,\n\t\t\t\t\t\t\t\t\t  final NotificationCompat.Builder builder,\n\t\t\t\t\t\t\t\t\t  final com.nononsenseapps.notepad.database.Notification note) {\n\t\t// create the intent that reacts to deleting the notification\n\t\tfinal Intent iDelete = new Intent(context, NotificationHelper.class)\n\t\t\t\t.setAction(Intent.ACTION_DELETE)\n\t\t\t\t.setData(note.getUri());\n\t\tif (note.isRepeating()) {\n\t\t\tiDelete.setAction(ACTION_RESCHEDULE);\n\t\t}\n\n\t\tiDelete.putExtra(ARG_TASKID, note.taskID);\n\t\t// Delete it on clear\n\t\tPendingIntent piDelete = PendingIntent.getBroadcast(context, 0,\n\t\t\t\tiDelete, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\n\t\t// Open intent\n\t\tfinal Intent openIntent = new Intent(Intent.ACTION_VIEW, Task.getUri(note.taskID));\n\t\t// Should create a new instance to avoid fragment problems\n\t\topenIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);\n\n\t\t// Repeating reminders should have a delete intent:\n\t\t// opening the note should delete/reschedule the notification\n\t\topenIntent.putExtra(NOTIFICATION_DELETE_ARG, note._id);\n\n\t\t// Opening always cancels the notification though\n\t\topenIntent.putExtra(NOTIFICATION_CANCEL_ARG, note._id);\n\n\t\t// Open note on click\n\t\tPendingIntent clickIntent = PendingIntent.getActivity(context, 0,\n\t\t\t\topenIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\n\t\t// Action to complete\n\t\tIntent iComplete = new Intent(context, NotificationHelper.class)\n\t\t\t\t.setAction(ACTION_COMPLETE)\n\t\t\t\t.setData(note.getUri())\n\t\t\t\t.putExtra(ARG_TASKID, note.taskID);\n\t\tPendingIntent piComplete = PendingIntent.getBroadcast(context, 0,\n\t\t\t\tiComplete, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\n\t\t// Action to snooze\n\t\tIntent iSnooze = new Intent(context, NotificationHelper.class)\n\t\t\t\t.setAction(ACTION_SNOOZE)\n\t\t\t\t.setData(note.getUri())\n\t\t\t\t.putExtra(ARG_TASKID, note.taskID);\n\t\tPendingIntent piSnooze = PendingIntent.getBroadcast(context, 0,\n\t\t\t\tiSnooze, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\n\t\t// Build notification\n\t\tbuilder.setContentTitle(note.taskTitle)\n\t\t\t\t.setContentText(note.taskNote)\n\t\t\t\t.setChannelId(CHANNEL_ID)\n\t\t\t\t.setContentIntent(clickIntent)\n\t\t\t\t.setStyle(new NotificationCompat.BigTextStyle().bigText(note.taskNote));\n\n\t\t// the Delete intent for non-location repeats\n\t\tbuilder.setDeleteIntent(piDelete);\n\n\t\t// Snooze button only on time reminders, not location-based reminders\n\t\tif (note.time != null) {\n\t\t\t// see ACTION_SNOOZE branch in onReceive()\n\t\t\tbuilder.addAction(R.drawable.ic_alarm_24dp, context.getText(R.string.snooze), piSnooze);\n\t\t}\n\n\t\tif (!note.isRepeating() && note.belongsToNoteInListOfTasks(context)) {\n\t\t\t// Show a complete button only on:\n\t\t\t// * non-repeating reminders. See issue #478\n\t\t\t// * reminders for task-types and not note-types, see #312\n\t\t\tbuilder.addAction(R.drawable.ic_check_24dp, context.getText(R.string.completed), piComplete);\n\t\t}\n\n\t\tfinal Notification noti = builder.build();\n\t\tnotificationManager.notify((int) note._id, noti);\n\t}\n\n\t/**\n\t * @return TRUE if notifications from this app will be visible to the user\n\t */\n\tpublic static boolean areNotificationsVisible(@NonNull NotificationManager manager) {\n\t\tboolean canShowNotif;\n\t\tif (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {\n\t\t\tcanShowNotif = manager.areNotificationsEnabled() && !manager.areNotificationsPaused();\n\t\t} else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {\n\t\t\tcanShowNotif = manager.areNotificationsEnabled();\n\t\t} else {\n\t\t\tcanShowNotif = true;\n\t\t}\n\t\treturn canShowNotif;\n\t}\n\n\tprivate static long getLatestTime(\n\t\t\tfinal List<com.nononsenseapps.notepad.database.Notification> notifications) {\n\t\tlong latest = 0;\n\t\tfor (com.nononsenseapps.notepad.database.Notification noti : notifications) {\n\t\t\tif (noti.time > latest) latest = noti.time;\n\t\t}\n\t\treturn latest;\n\t}\n\n\t/**\n\t * Schedules this {@link BroadcastReceiver} to be woken up at the next notification time.\n\t * Uses {@link AlarmManager}, which can set alarms with different priorities.\n\t * See <a href=\"https://developer.android.com/training/scheduling/alarms#exact\">this page</a>.\n\t * You can't expect android to be precise or reliable: reminders will appear within\n\t * a few minutes from the specified time, or may not appear at all until the app\n\t * is restarted. OEM and vendors make this impossibile to solve\n\t */\n\tprivate static void scheduleNext(Context context) {\n\t\t// Get first future notification\n\t\tfinal Calendar now = Calendar.getInstance();\n\t\tfinal List<com.nononsenseapps.notepad.database.Notification> notifications\n\t\t\t\t= com.nononsenseapps.notepad.database.Notification\n\t\t\t\t.getNotificationsWithTime(context, now.getTimeInMillis(), false);\n\n\t\t// TODO check these:\n\t\t//  https://developer.android.com/reference/android/Manifest.permission#SCHEDULE_EXACT_ALARM\n\t\t//  https://developer.android.com/reference/android/Manifest.permission#USE_EXACT_ALARM\n\n\t\t// if not empty, schedule alarm wake up\n\t\tif (!notifications.isEmpty()) {\n\t\t\t// at first's time\n\t\t\tvar thingToNotify = notifications.get(0);\n\n\t\t\t// must be an explicit intent\n\t\t\tIntent intent = new Intent(context, NotificationHelper.class)\n\t\t\t\t\t.addFlags(Intent.FLAG_RECEIVER_FOREGROUND) // useless flag => remove freely\n\t\t\t\t\t.setAction(Intent.ACTION_RUN);\n\t\t\t// Create a new PendingIntent and add it to the AlarmManager\n\t\t\tvar pendingIntent = PendingIntent.getBroadcast(context, 1, intent,\n\t\t\t\t\tPendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\t\t\tAlarmManager aMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);\n\t\t\taMgr.cancel(pendingIntent);\n\n\t\t\tif (useExactReminders(context, aMgr)) {\n\t\t\t\t// an \"exact\" alarm is more reliable, but requires user permission in API 31\n\t\t\t\taMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,\n\t\t\t\t\t\tgetTimeForAlarm(thingToNotify), pendingIntent);\n\t\t\t\t/*\n\t\t\t\tThere is also .setAlarmClock(), but it didn't work when I tried.\n\t\t\t\tIt's has the highest priority, but the OS may show the reminder's trigger\n\t\t\t\ttime in the statubar on top of the screen. Therefore it overwrites any\n\t\t\t\talarm set by the clock app (waking up, bed time, ...). Since many\n\t\t\t\tusers (me, at least) are more interested in seeing those on the status bar,\n\t\t\t\t(and not this app's reminders), we should avoid using setAlarmClock().\n\t\t\t\tIn any case, it would look like this:\n\t\t\t\taMgr.setAlarmClock(new AlarmManager.AlarmClockInfo(\n\t\t\t\t\tgetTimeForAlarm(thingToNotify), new PendingIntent(...)), pendingIntent);\n\t\t\t\t*/\n\t\t\t} else {\n\t\t\t\t// these kinds of alarms don't require permission, but they are more vague\n\t\t\t\taMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,\n\t\t\t\t\t\tgetTimeForAlarm(thingToNotify), pendingIntent);\n\t\t\t}\n\t\t}\n\t\t// old function deleted in december 2024. It was causing issue #543\n\t\t// monitorUri(context);\n\t}\n\n\t/**\n\t * @return TRUE if the Alarms (reminders) should be sent with the Exact method,\n\t * which is more reliable and precise but heavier on the battery. The user can choose this\n\t * in the preferences, and the OS may deny us the permission to do it\n\t */\n\tprivate static boolean useExactReminders(Context context, AlarmManager aMgr) {\n\t\t// The user can (must) request the use of Exact alarms\n\t\tboolean shouldUseExact = PreferencesHelper.shouldUseExactAlarms(context);\n\t\tif (!shouldUseExact) return false;\n\n\t\t// the user may revoke the permission in android 12\n\t\tboolean canUseExact;\n\t\tif (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {\n\t\t\tcanUseExact = aMgr.canScheduleExactAlarms();\n\t\t} else {\n\t\t\t// in older androids, we can always use Exact reminders\n\t\t\tcanUseExact = true;\n\t\t}\n\t\t// it SHOULD and CAN do it\n\t\treturn canUseExact;\n\t}\n\n\t/**\n\t * @return the time to start the alarm, of the System.currentTimeMillis() type,\n\t * so a {@link Long} representing a \"wall clock time\" in UTC\n\t */\n\tprivate static long getTimeForAlarm(com.nononsenseapps.notepad.database.Notification input) {\n\t\t// TODO since android takes some time to understand that it has to send the reminder,\n\t\t//  here we could subtract 60~100 seconds so that, by the time it understands what to\n\t\t//  do, we're not late with the reminder. Seems paranoic, though\n\t\treturn input.time; // - 60 * 1000\n\t}\n\n\t/**\n\t * Schedules coming notifications, and displays expired ones.\n\t * Only notififies once for existing notifications.\n\t */\n\tpublic static void schedule(final Context context) {\n\t\tnotifyPast(context);\n\t\tscheduleNext(context);\n\t}\n\n\t/**\n\t * Updates or Inserts the given Notification in the database. Immediately notifies and\n\t * schedules next wake up on finish.\n\t */\n\tprivate static void updateNotification(final Context context,\n\t\t\t\t\t\t\t\t\t\t   final com.nononsenseapps.notepad.database.Notification notification) {\n\t\t// Avoid INSERTing only if update is success. This way the editor can update\n\t\t// a deleted notification and still have it persisted in the database\n\t\tboolean shouldInsert = true;\n\t\t// If id is -1, then this should be inserted\n\t\tif (notification._id > 0) {\n\t\t\t// Else it should be updated\n\t\t\tint result = notification.save(context);\n\t\t\tif (result > 0) shouldInsert = false;\n\t\t}\n\n\t\tif (shouldInsert) {\n\t\t\tnotification._id = -1;\n\t\t\tnotification.save(context);\n\t\t}\n\n\t\tschedule(context);\n\t}\n\n\t/**\n\t * Deletes the indicated notification from the notification tray. Does not touch database.\n\t * Called by {@link com.nononsenseapps.notepad.database.Notification#delete}\n\t */\n\tpublic static void cancelNotification(final Context context,\n\t\t\t\t\t\t\t\t\t\t  final com.nononsenseapps.notepad.database.Notification not) {\n\t\tif (not != null) {\n\t\t\tcancelNotification(context, not.getUri());\n\t\t}\n\t}\n\n\t/**\n\t * removes the ANDROID notification: IT does not touch the db record\n\t */\n\tpublic static void cancelNotification(final Context context, final Uri uri) {\n\t\tif (uri == null) return;\n\t\tcancelNotification(context, Integer.parseInt(uri.getLastPathSegment()));\n\t}\n\n\t/**\n\t * removes the ANDROID notification: IT does not touch the db record\n\t */\n\tpublic static void cancelNotification(final Context context, final int notId) {\n\t\tfinal NotificationManager notificationManager = (NotificationManager) context\n\t\t\t\t.getSystemService(Context.NOTIFICATION_SERVICE);\n\t\tnotificationManager.cancel(notId);\n\t}\n\n\t/**\n\t * Given a list of notifications, returns a list of the lists the notes\n\t * belong to.\n\t */\n\tprivate static Collection<Long> getRelatedLists(\n\t\t\tfinal List<com.nononsenseapps.notepad.database.Notification> notifications) {\n\t\tfinal HashSet<Long> lists = new HashSet<>();\n\t\tfor (com.nononsenseapps.notepad.database.Notification not : notifications) {\n\t\t\tlists.add(not.listID);\n\t\t}\n\n\t\treturn lists;\n\t}\n\n\t/**\n\t * Returns a list of those notifications that are associated to notes in the\n\t * specified list.\n\t */\n\tprivate static List<com.nononsenseapps.notepad.database.Notification> getSubList(\n\t\t\tfinal long listId,\n\t\t\tfinal List<com.nononsenseapps.notepad.database.Notification> notifications) {\n\t\tfinal ArrayList<com.nononsenseapps.notepad.database.Notification> subList = new ArrayList<>();\n\t\tfor (com.nononsenseapps.notepad.database.Notification not : notifications) {\n\t\t\tif (not.listID == listId) {\n\t\t\t\tsubList.add(not);\n\t\t\t}\n\t\t}\n\n\t\treturn subList;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/PermissionsHelper.java",
    "content": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\n\n/**\n * Helper class which handles runtime permissions.\n */\npublic final class PermissionsHelper {\n\n\t/**\n\t * Permissions to show notifications\n\t */\n\tpublic static final String[] FOR_NOTIFICATIONS =\n\t\t\tnew String[] { \"android.permission.POST_NOTIFICATIONS\" };\n\n\tpublic static final int REQCODE_NOTIFICATIONS = 3;\n\n\t/**\n\t * @return TRUE if all the specified permissions are granted, FALSE otherwise\n\t */\n\tpublic static boolean hasPermissions(@NonNull Context context, String... permissions) {\n\t\tfor (String permission : permissions) {\n\t\t\tboolean hasPermission = PackageManager.PERMISSION_GRANTED\n\t\t\t\t\t== ContextCompat.checkSelfPermission(context, permission);\n\t\t\tif (!hasPermission) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param permissions  that were requested\n\t * @param grantResults of the request\n\t * @return true if all results were granted, false otherwise\n\t */\n\tpublic static boolean permissionsGranted(@NonNull String[] permissions,\n\t\t\t\t\t\t\t\t\t\t\t @NonNull int[] grantResults) {\n\t\treturn permissions.length > 0 && allGranted(grantResults);\n\t}\n\n\tprivate static boolean allGranted(int[] items) {\n\t\tfor (int item : items) {\n\t\t\tif (PackageManager.PERMISSION_GRANTED != item) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/PreferencesHelper.java",
    "content": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.provider.Settings;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.prefs.PasswordPrefs;\nimport com.nononsenseapps.notepad.prefs.SyncPrefs;\n\n/**\n * Helper class to save common options to shared preferences.\n */\npublic final class PreferencesHelper {\n\n\tprivate static SharedPreferences Prefs(@NonNull Context context) {\n\t\treturn PreferenceManager.getDefaultSharedPreferences(context);\n\t}\n\n\tpublic static boolean shouldUseExactAlarms(@NonNull Context context) {\n\t\tString key = context.getString(R.string.key_pref_should_use_exact_alarms);\n\t\treturn Prefs(context).getBoolean(key, false);\n\t}\n\n\tpublic static boolean isSdSyncEnabled(@NonNull Context context) {\n\t\treturn Prefs(context).getBoolean(SyncPrefs.KEY_SD_ENABLE, false);\n\t}\n\n\t/**\n\t * Disable SD synchronization in the settings\n\t */\n\tpublic static void disableSdCardSync(@NonNull Context context) {\n\t\tPrefs(context).edit().putBoolean(SyncPrefs.KEY_SD_ENABLE, false).apply();\n\t}\n\n\t/**\n\t * @return TRUE if synchronization is enabled in the global preference,\n\t * regardless of any sync method chosen. This being false should block all sync code\n\t * from running at all\n\t */\n\tpublic static boolean isSincEnabledAtAll(@NonNull Context context) {\n\t\tString key = context.getString(R.string.key_pref_sync_enabled_master);\n\t\treturn Prefs(context).getBoolean(key, false);\n\t}\n\n\tprivate static String getStr(@NonNull Context c, int id) {\n\t\treturn c.getResources().getString(id);\n\t}\n\n\tpublic static void setSortingDue(@NonNull Context context) {\n\t\tPrefs(context)\n\t\t\t\t.edit()\n\t\t\t\t.putString(getStr(context, R.string.pref_sorttype), getStr(context, R.string.const_duedate))\n\t\t\t\t.commit();\n\t}\n\n\tpublic static void setSortingManual(@NonNull Context context) {\n\t\tPrefs(context)\n\t\t\t\t.edit()\n\t\t\t\t.putString(getStr(context, R.string.pref_sorttype), getStr(context, R.string.const_possubsort))\n\t\t\t\t.commit();\n\t}\n\n\tpublic static void setSortingAlphabetic(Context context) {\n\t\tPrefs(context)\n\t\t\t\t.edit()\n\t\t\t\t.putString(getStr(context, R.string.pref_sorttype), getStr(context, R.string.const_alphabetic))\n\t\t\t\t.commit();\n\t}\n\n\n\tprivate static void put(@NonNull Context context, @NonNull String key, @NonNull String value) {\n\t\tPrefs(context).edit().putString(key, value).apply();\n\t}\n\n\tpublic static void put(@NonNull Context context, @NonNull String key, boolean value) {\n\t\tPrefs(context).edit().putBoolean(key, value).apply();\n\t}\n\n\t/**\n\t * @return TRUE if the password for locking notes is set, FALSE if it isn't\n\t */\n\tpublic static boolean isPasswordSet(@NonNull Context context) {\n\t\treturn !Prefs(context).getString(PasswordPrefs.KEY_PASSWORD, \"\").isEmpty();\n\t}\n\n\t/**\n\t * @return TRUE if animations are enabled in the system settings. Used to choose if animations\n\t * should be displayed in the app\n\t */\n\tpublic static boolean areAnimationsEnabled(@NonNull Context context) {\n\n\t\t// there are 3 redundant system settings that control animations\n\t\tString[] sysSettingsToCheck = new String[] {\n\t\t\t\tSettings.Global.ANIMATOR_DURATION_SCALE,\n\t\t\t\tSettings.Global.TRANSITION_ANIMATION_SCALE,\n\t\t\t\tSettings.Global.WINDOW_ANIMATION_SCALE\n\t\t};\n\n\t\t// if at least 1 of those is set to \"0x\", which means \"disable animations\",\n\t\t// we assume that the user wants to disable all animations, also in this app\n\t\tfor (String option : sysSettingsToCheck) {\n\t\t\tfloat f = Settings.Global.getFloat(context.getContentResolver(), option, 1.0f);\n\t\t\tif (f == 0) return false;\n\t\t}\n\n\t\t// if none of the 3 settings is \"0x\", we assume that animations are enabled\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/RFC3339Date.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\npublic final class RFC3339Date {\n\n\t// TODO see TimeFormatter & TimeHelper in this package. One of these 3 classes HAS to be redundant\n\n\tpublic static java.util.Date parseRFC3339Date(String datestring) {\n\t\tif (datestring == null || datestring.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tDate d;\n\n\t\t// if there is no time zone, we don't need to do any special parsing.\n\t\tif (datestring.endsWith(\"Z\")) {\n\t\t\ttry {\n\t\t\t\tSimpleDateFormat s = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss'Z'\",\n\t\t\t\t\t\tLocale.US); // spec for RFC3339\n\t\t\t\ts.setCalendar(Calendar.getInstance(TimeZone.getTimeZone(\"UTC\")));\n\t\t\t\td = s.parse(datestring);\n\t\t\t} catch (ParseException pe) { // try again with optional\n\t\t\t\t// decimals\n\t\t\t\tSimpleDateFormat s = new SimpleDateFormat(\n\t\t\t\t\t\t\"yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'\",\n\t\t\t\t\t\tLocale.US); // spec for RFC3339\n\t\t\t\t// (with fractional seconds)\n\t\t\t\ts.setCalendar(Calendar.getInstance(TimeZone.getTimeZone(\"UTC\")));\n\t\t\t\ts.setLenient(true);\n\t\t\t\ttry {\n\t\t\t\t\td = s.parse(datestring);\n\t\t\t\t} catch (ParseException e) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn d;\n\t\t}\n\n\t\tSimpleDateFormat s = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssZ\", Locale.US);\n\t\t// spec for RFC3339\n\t\ts.setCalendar(Calendar.getInstance(TimeZone.getTimeZone(\"UTC\")));\n\n\t\ttry {\n\t\t\td = s.parse(datestring);\n\t\t} catch (java.text.ParseException pe) { // try again with optional decimals\n\t\t\ts = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ\", Locale.US);\n\t\t\ts.setCalendar(Calendar.getInstance(TimeZone.getTimeZone(\"UTC\")));\n\t\t\t// spec for RFC3339(with fractional seconds)\n\t\t\ts.setLenient(true);\n\t\t\ttry {\n\t\t\t\td = s.parse(datestring);\n\t\t\t} catch (ParseException e) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\treturn d;\n\t}\n\n\t/**\n\t * Given a UTC date (2013-02-21), and a local time(13:23), will combine them\n\t * into 2013-02-21T13:23 local time.\n\t *\n\t * DateString should be in RFC3339.\n\t * If time is null, defaults to 23:59\n\t */\n\tpublic static Long combineDateAndTime(final String datestring, final Long time) {\n\t\tfinal java.util.Date d = parseRFC3339Date(datestring);\n\t\tif (d == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// UTC\n\t\tfinal Calendar utc = GregorianCalendar.getInstance(TimeZone.getTimeZone(\"UTC\"));\n\t\tutc.setTime(d);\n\t\tutc.set(Calendar.HOUR_OF_DAY, 0);\n\t\tutc.set(Calendar.MINUTE, 0);\n\n\t\t// Local date\n\t\tfinal Calendar local = GregorianCalendar.getInstance();\n\t\tlocal.set(Calendar.YEAR, utc.get(Calendar.YEAR));\n\t\tlocal.set(Calendar.MONTH, utc.get(Calendar.MONTH));\n\t\tlocal.set(Calendar.DAY_OF_MONTH, utc.get(Calendar.DAY_OF_MONTH));\n\t\t// Default to 23:59\n\t\tlocal.set(Calendar.MINUTE, 59);\n\t\tlocal.set(Calendar.HOUR_OF_DAY, 23);\n\n\t\t// Time\n\t\tif (time == null) {\n\t\t\treturn local.getTimeInMillis();\n\t\t}\n\n\t\tfinal Calendar localTime = GregorianCalendar.getInstance();\n\t\tlocalTime.setTimeInMillis(time);\n\n\t\tlocal.set(Calendar.MINUTE, localTime.get(Calendar.MINUTE));\n\t\tlocal.set(Calendar.HOUR_OF_DAY, localTime.get(Calendar.HOUR_OF_DAY));\n\n\t\treturn local.getTimeInMillis();\n\t}\n\n\tpublic static String asRFC3339(final Long time) {\n\t\tif (time == null)\n\t\t\treturn null;\n\t\treturn asRFC3339(new Date(time));\n\t}\n\n\t/**\n\t * For GTasks syncing. Given a date and time, say 2013-02-21T13:34.\n\t * Will return 2013-02-21T00:00Z.\n\t */\n\tpublic static String asRFC3339ZuluDate(final Long time) {\n\t\tif (time == null)\n\t\t\treturn null;\n\n\t\t// Local time calendar\n\t\tCalendar cal = GregorianCalendar.getInstance();\n\t\tcal.setTimeInMillis(time);\n\n\t\t// Extract the date\n\t\treturn String.format(Locale.US, \"%d\", cal.get(Calendar.YEAR)) +\n\t\t\t\t\"-\" +\n\t\t\t\tString.format(Locale.US, \"%02d\", (1 + cal.get(Calendar.MONTH))) +\n\t\t\t\t\"-\" +\n\t\t\t\tString.format(Locale.US, \"%02d\", cal.get(Calendar.DAY_OF_MONTH)) +\n\t\t\t\t\"T00:00:00Z\";\n\t}\n\n\tprivate static String asRFC3339(final java.util.Date date) {\n\t\tif (date == null) return null;\n\t\t// spec for RFC3339:\n\t\tfinal SimpleDateFormat s =\n\t\t\t\tnew SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssZ\", Locale.US);\n\t\treturn s.format(date);\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/SyncStatusMonitor.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.Bundle;\nimport android.widget.Toast;\n\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.content.ContextCompat;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.sync.SyncAdapter;\n\npublic final class SyncStatusMonitor extends BroadcastReceiver {\n\n\tAppCompatActivity activity;\n\tOnSyncStartStopListener listener;\n\n\t/**\n\t * Call this in the activity's onResume\n\t */\n\tpublic void startMonitoring(AppCompatActivity activity, OnSyncStartStopListener listener) {\n\t\t// in the caller, the activity acts also as the listener, anyway\n\t\tthis.activity = activity;\n\t\tthis.listener = listener;\n\n\t\tContextCompat.registerReceiver(activity, this, new IntentFilter(SyncAdapter.SYNC_FINISHED), ContextCompat.RECEIVER_NOT_EXPORTED);\n\t\tContextCompat.registerReceiver(activity, this, new IntentFilter(SyncAdapter.SYNC_STARTED), ContextCompat.RECEIVER_NOT_EXPORTED);\n\n\t\tif (!PreferencesHelper.isSincEnabledAtAll(activity)) {\n\t\t\t// not starting: sync is disabled in the prefs\n\t\t\treturn;\n\t\t}\n\n\t\t// get the selected account and verify if it is valid\n\t\tboolean isAccountValid = false;\n\t\t// Sync state might have changed, make sure we're spinning when we should\n\t\ttry {\n\t\t\tlistener.onSyncStartStop(isAccountValid);\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.debug(SyncStatusMonitor.class, e.getMessage());\n\t\t}\n\t}\n\n\t/**\n\t * Call this in the activity's onPause\n\t */\n\tpublic void stopMonitoring() {\n\t\ttry {\n\t\t\tactivity.unregisterReceiver(this);\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.exception(e);\n\t\t}\n\t\ttry {\n\t\t\tlistener.onSyncStartStop(false);\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.exception(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onReceive(final Context context, final Intent intent) {\n\n\t\tif (!PreferencesHelper.isSincEnabledAtAll(context)) {\n\t\t\tNnnLogger.debug(SyncStatusMonitor.class,\n\t\t\t\t\t\"ignore onReceive(): sync is disabled in the prefs\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (intent.getAction().equals(SyncAdapter.SYNC_STARTED)) {\n\t\t\tactivity.runOnUiThread(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tlistener.onSyncStartStop(true);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tNnnLogger.exception(e);\n\t\t\t\t}\n\t\t\t});\n\t\t} else { //if (intent.getAction().equals(SyncAdapter.SYNC_FINISHED)) {\n\t\t\tactivity.runOnUiThread(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tlistener.onSyncStartStop(false);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tNnnLogger.exception(e);\n\t\t\t\t}\n\t\t\t});\n\t\t\tBundle b = intent.getExtras();\n\t\t\tif (b == null) {\n\t\t\t\tb = Bundle.EMPTY;\n\t\t\t}\n\t\t\ttellUser(context, b.getInt(SyncAdapter.SYNC_RESULT, SyncAdapter.SUCCESS));\n\t\t}\n\t}\n\n\tprivate void tellUser(Context context, int result) {\n\t\tint text;\n\t\tswitch (result) {\n\t\t\tcase SyncAdapter.ERROR -> text = R.string.sync_failed;\n\t\t\tcase SyncAdapter.LOGIN_FAIL -> text = R.string.sync_login_failed;\n\t\t\tdefault -> {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tNnnLogger.debug(SyncStatusMonitor.class, \"SYNC: \" + result);\n\t\tToast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);\n\t\ttoast.show();\n\t}\n\n\tpublic interface OnSyncStartStopListener {\n\t\t/**\n\t\t * This is always called on the activity's UI thread.\n\t\t */\n\t\tvoid onSyncStartStop(final boolean ongoing);\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/ThemeHelper.java",
    "content": "package com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.util.TypedValue;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.prefs.AppearancePrefs;\n\n/**\n * contains functions that handle themes and related resources\n */\npublic final class ThemeHelper {\n\n\t// forbid instances: it's a static class\n\tprivate ThemeHelper() {}\n\n\t/**\n\t * It is different from {@link R.color#accent} if the user sets a custom Material3\n\t * theme in android 13 or greater\n\t *\n\t * @return This theme's accent color, in the form 0xAARRGGBB\n\t */\n\t@ColorInt\n\tpublic static int getThemeAccentColor(@NonNull Context context) {\n\t\tvar outValue = new TypedValue();\n\t\tcontext.getTheme()\n\t\t\t\t.resolveAttribute(android.R.attr.colorAccent, outValue, true);\n\t\treturn outValue.data;\n\t}\n\n\t/**\n\t * Set the theme chosen by the user in {@link AppearancePrefs} for this activity\n\t */\n\tpublic static void setTheme(@NonNull AppCompatActivity activity) {\n\t\tfinal SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);\n\t\tfinal String theme = prefs.getString(AppearancePrefs.KEY_THEME,\n\t\t\t\tactivity.getString(R.string.const_theme_light_ab));\n\t\tif (activity.getString(R.string.const_theme_light_ab).equals(theme)) {\n\t\t\tactivity.setTheme(R.style.ThemeNnnLight);\n\t\t} else if (activity.getString(R.string.const_theme_black).equals(theme)) {\n\t\t\tactivity.setTheme(R.style.ThemeNnnPitchBlack);\n\t\t} else if (activity.getString(R.string.const_theme_classic).equals(theme)) {\n\t\t\tactivity.setTheme(R.style.ThemeNnnClassicLight);\n\t\t} else if (theme.equals(activity.getResources().getString(R.string.const_theme_googlenow_dark))) {\n\t\t\tactivity.setTheme(R.style.ThemeNnnDark);\n\t\t} else {\n\t\t\t// any theme you want to add should go in a new if block ~here\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/TimeFormatter.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\n\nimport java.text.SimpleDateFormat;\nimport java.time.LocalDate;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Locale;\n\n/**\n * A class that helps with displaying locale and preference specific dates\n */\npublic final class TimeFormatter {\n\n\tpublic static final String WEEKDAY_SHORTEST_FORMAT = \"E\";\n\n\tpublic static Locale getLocale(final String lang) {\n\t\tfinal Locale locale;\n\t\tif (lang == null || lang.isEmpty()) {\n\t\t\tlocale = Locale.getDefault();\n\t\t} else if (lang.length() == 5) {\n\t\t\tlocale = new Locale(lang.substring(0, 2), lang.substring(3, 5));\n\t\t} else {\n\t\t\tlocale = new Locale(lang.substring(0, 2));\n\t\t}\n\t\treturn locale;\n\t}\n\n\t/**\n\t * Formats date according to the designated locale\n\t */\n\tpublic static String getLocalDateString(final Context context, final String lang,\n\t\t\t\t\t\t\t\t\t\t\tfinal String format, final long timeInMillis) {\n\t\treturn getLocalFormatter(context, lang, format).format(\n\t\t\t\tnew Date(timeInMillis));\n\t}\n\n\t/**\n\t * Formats the date according to the locale the user has defined in settings\n\t */\n\tpublic static String getLocalDateString(final Context context,\n\t\t\t\t\t\t\t\t\t\t\tfinal String format, final long timeInMillis) {\n\t\treturn getLocalDateString(\n\t\t\t\tcontext,\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\tformat, timeInMillis);\n\t}\n\n\t/**\n\t * Dont use for performance critical settings\n\t */\n\tpublic static String getLocalDateStringLong(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t\tfinal long time) {\n\t\treturn getLocalFormatterLong(context).format(new Date(time));\n\t}\n\n\tpublic static String getLocalDateOnlyStringLong(final Context context, final long time) {\n\t\treturn getLocalFormatterLongDateOnly(context).format(new Date(time));\n\t}\n\n\tpublic static String getLocalTimeOnlyString(final Context context, final long time) {\n\t\tfinal String format;\n\t\tif (android.text.format.DateFormat.is24HourFormat(context)) {\n\t\t\t// 00:59\n\t\t\tformat = \"HH:mm\";\n\t\t} else {\n\t\t\t// 12:59 am\n\t\t\tformat = \"h:mm a\";\n\t\t}\n\t\t// like \"it_IT\"\n\t\tString localePrefVal = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context)\n\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\");\n\t\treturn new SimpleDateFormat(format, getLocale(localePrefVal)).format(new Date(time));\n\t}\n\n\t/**\n\t * Dont use for performance critical settings\n\t */\n\tpublic static String getLocalDateStringShort(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t\t final long time) {\n\t\treturn getLocalFormatterShort(context).format(new Date(time));\n\t}\n\n\t/**\n\t * Replaces first \"localtime\" in format string to a time which respects\n\t * global 24h setting.\n\t */\n\tprivate static String withSuitableTime(final Context context, final String formatString) {\n\t\tif (android.text.format.DateFormat.is24HourFormat(context)) {\n\t\t\t// 00:59\n\t\t\treturn formatString.replaceFirst(\"localtime\", \"HH:mm\");\n\t\t} else {\n\t\t\t// 12:59 am\n\t\t\treturn formatString.replaceFirst(\"localtime\", \"h:mm a\");\n\t\t}\n\t}\n\n\t/**\n\t * Removes \"localtime\" in format string\n\t */\n\tprivate static String withSuitableDateOnly(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t   final String formatString) {\n\t\treturn formatString.replaceAll(\"\\\\s*localtime\\\\s*\", \" \");\n\t}\n\n\t/**\n\t * @param lang if app is in japanese, it's \"ja\" and uses values-ja/strings.xml\n\t */\n\tprivate static SimpleDateFormat getLocalFormatter(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  final String lang, final String format) {\n\t\tfinal Locale locale = getLocale(lang);\n\t\tSimpleDateFormat sdf;\n\t\ttry {\n\t\t\t//noinspection UnusedAssignment\n\t\t\tsdf = new SimpleDateFormat(withSuitableTime(context, format), locale);\n\t\t} catch (IllegalArgumentException iae) {\n\t\t\tNnnLogger.error(TimeFormatter.class,\n\t\t\t\t\t\"Error in translated date format strings. In: values-\" + lang\n\t\t\t\t\t\t\t+ \", value: \" + format);\n\t\t\tNnnLogger.exception(iae);\n\t\t} finally {\n\t\t\t// just log the error, but crash anyway\n\t\t\tsdf = new SimpleDateFormat(withSuitableTime(context, format), locale);\n\t\t}\n\t\treturn sdf;\n\t}\n\n\tpublic static GregorianCalendar getLocalCalendar(final Context context) {\n\t\tvar prefs = PreferenceManager.getDefaultSharedPreferences(context);\n\t\tfinal Locale locale = getLocale(prefs\n\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"));\n\t\treturn new GregorianCalendar(locale);\n\t}\n\n\t/**\n\t * Good for performance critical situations, like lists\n\t */\n\tpublic static SimpleDateFormat getLocalFormatterLong(final Context context) {\n\t\treturn getLocalFormatter(\n\t\t\t\tcontext,\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\twithSuitableTime(\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\tPreferenceManager\n\t\t\t\t\t\t\t\t.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t\t\t.getString(\n\t\t\t\t\t\t\t\t\t\tcontext.getString(R.string.key_pref_dateformat_long),\n\t\t\t\t\t\t\t\t\t\tcontext.getString(R.string.dateformat_long_1))));\n\t}\n\n\tpublic static SimpleDateFormat getDateFormatter(final Context context) {\n\t\treturn getLocalFormatter(\n\t\t\t\tcontext,\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\tcontext.getString(R.string.dateformat_just_date));\n\t}\n\n\tpublic static SimpleDateFormat getLocalFormatterLongDateOnly(final Context context) {\n\t\treturn getLocalFormatter(\n\t\t\t\tcontext,\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\twithSuitableDateOnly(\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\tPreferenceManager\n\t\t\t\t\t\t\t\t.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t\t\t.getString(\n\t\t\t\t\t\t\t\t\t\tcontext.getString(R.string.key_pref_dateformat_long),\n\t\t\t\t\t\t\t\t\t\tcontext.getString(R.string.dateformat_long_1))));\n\t}\n\n\t/**\n\t * Good for performance critical situations, like lists\n\t */\n\tpublic static SimpleDateFormat getLocalFormatterShort(final Context context) {\n\t\tString userDateFormat = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context)\n\t\t\t\t.getString(context.getString(R.string.key_pref_dateformat_short),\n\t\t\t\t\t\tcontext.getString(R.string.dateformat_short_1));\n\t\treturn getLocalFormatter(context,\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\twithSuitableTime(context, userDateFormat)); // <-- notice this\n\t}\n\n\t/**\n\t * Returns the format chosen by the user to show dates in the list view. For example,\n\t * a timestamp 1754752027 could be displayed as \"sat 9 aug 2025\" or \"sat, 9 aug\"\n\t */\n\tpublic static SimpleDateFormat getLocalFormatterShortDateOnly(final Context context) {\n\n\t\tSharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);\n\t\tString userFavLocale = pref.getString(context.getString(R.string.pref_locale), \"\");\n\t\tString userFavShortDateFormat = pref.getString(\n\t\t\t\tcontext.getString(R.string.key_pref_dateformat_short),\n\t\t\t\tcontext.getString(R.string.dateformat_short_2));\n\n\t\treturn getLocalFormatter(context, userFavLocale,\n\t\t\t\twithSuitableDateOnly( // <-- notice this!\n\t\t\t\t\t\tcontext, userFavShortDateFormat));\n\t}\n\n\tpublic static SimpleDateFormat getLocalFormatterMicro(final Context context) {\n\t\treturn getLocalFormatter(\n\t\t\t\tcontext,\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\twithSuitableTime(context,\n\t\t\t\t\t\tcontext.getString(R.string.dateformat_micro)));\n\t}\n\n\t/**\n\t * Good for performance critical situations, like lists\n\t */\n\tpublic static SimpleDateFormat getLocalFormatterWeekday(final Context context) {\n\t\treturn getLocalFormatter(context,\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\tcontext.getString(R.string.dateformat_weekday));\n\t}\n\n\t/**\n\t * Good for performance critical situations, like lists\n\t */\n\tpublic static SimpleDateFormat getLocalFormatterWeekdayShort(final Context context) {\n\t\treturn getLocalFormatter(context,\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(context)\n\t\t\t\t\t\t.getString(context.getString(R.string.pref_locale), \"\"),\n\t\t\t\tWEEKDAY_SHORTEST_FORMAT);\n\t}\n\n\t/**\n\t * @return how many days the next month will have, so 28 for february and so on\n\t */\n\tpublic static int getHowManyDaysInTheNextMonth() {\n\t\tCalendar x = Calendar.getInstance();\n\t\tx.add(Calendar.MONTH, 1);\n\t\treturn x.getActualMaximum(Calendar.DAY_OF_MONTH);\n\t}\n\n\t/**\n\t * @return the number of days from today until the beginning of the next month,\n\t * so 15 if this is being run on 14 feb 2023, because we consider 1 mar 2023\n\t */\n\tpublic static int getHowManyDaysUntilFirstOfNextMonth() {\n\t\tLocalDate today = LocalDate.now();\n\t\tLocalDate endOfMonth = today.withDayOfMonth(today.lengthOfMonth());\n\t\tlong daysBetween = java.time.temporal.ChronoUnit.DAYS.between(today, endOfMonth);\n\t\treturn (int) daysBetween + 1;\n\t}\n\n\t/**\n\t * @return same as {@link #getHowManyDaysInTheNextMonth()} but for the year\n\t */\n\tpublic static int getHowManyDaysInNextYear() {\n\t\tCalendar x = Calendar.getInstance();\n\t\tx.add(Calendar.YEAR, 1);\n\t\treturn x.getActualMaximum(Calendar.DAY_OF_YEAR);\n\t}\n\n\t/**\n\t * @return same as {@link #getHowManyDaysUntilFirstOfNextMonth} but for the year\n\t */\n\tpublic static int getHowManyDaysUntilFirstOfNextYear() {\n\t\tLocalDate today = LocalDate.now();\n\t\tLocalDate endOfYear = today.withDayOfYear(today.lengthOfYear());\n\t\tlong daysBetween = java.time.temporal.ChronoUnit.DAYS.between(today, endOfYear);\n\t\treturn (int) daysBetween + 1;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/UpdateNotifier.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.helpers;\n\nimport android.appwidget.AppWidgetManager;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.net.Uri;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Notification;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.widget.list.ListWidgetProvider;\nimport com.nononsenseapps.notepad.widget.list.WidgetPrefs;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * The purpose here is to make it easy for other classes to notify that\n * something has changed in the database. Will also call update on the widgets\n * appropriately.\n */\npublic final class UpdateNotifier {\n\n\t/**\n\t * Will update all notes and specific uri if present\n\t */\n\tpublic static void notifyChangeNote(Context context) {\n\t\tnotifyChange(context, Task.URI);\n\t\tupdateWidgets(context);\n\t}\n\n\t/**\n\t * Will update all notes and specific uri if present\n\t *\n\t * @param uri optional uri\n\t */\n\tpublic static void notifyChangeNote(Context context, Uri uri) {\n\t\tnotifyChange(context, uri);\n\t\tnotifyChangeNote(context);\n\t}\n\n\t/**\n\t * Will update all notes\n\t */\n\tpublic static void notifyChangeList(Context context) {\n\t\tnotifyChange(context, TaskList.URI);\n\t\tupdateWidgets(context);\n\t}\n\n\t/**\n\t * Will update all lists and specific uri if present\n\t *\n\t * @param uri optional uri\n\t */\n\tpublic static void notifyChangeList(Context context, Uri uri) {\n\t\tnotifyChange(context, uri);\n\t\tnotifyChangeList(context);\n\t}\n\n\t/**\n\t * Will update all lists and specific uri if present\n\t * Always updates notifications\n\t *\n\t * @param uri optional uri\n\t */\n\tprivate static void notifyChange(Context context, Uri uri) {\n\t\tif (uri == null) return;\n\t\tcontext.getContentResolver().notifyChange(uri, null, false);\n\t\tcontext.getContentResolver().notifyChange(Notification.URI, null);\n\t}\n\n\t/**\n\t * Runs code in a separate thread\n\t */\n\tprivate static final ExecutorService mExecutor = Executors.newSingleThreadExecutor();\n\n\t/**\n\t * Instead of doing this in a service which might be killed, simply call\n\t * this whenever something is changed in here\n\t *\n\t * Update all widgets's views as this database has changed somehow\n\t */\n\tpublic static void updateWidgets(Context context) {\n\t\tfinal AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);\n\t\tint[] appWidgetIds = appWidgetManager.getAppWidgetIds(\n\t\t\t\tnew ComponentName(context, ListWidgetProvider.class));\n\n\t\t// .notifyAppWidgetViewDataChanged() is a very slow function that takes 40 seconds for\n\t\t// each list widget present in the launcher. We run it in a background thread. This way,\n\t\t// we ensure that, when the user taps a checkbox in the app, the note is recognized as\n\t\t// completed right away, without waiting for every list-widget to update. This fixes #574.\n\t\t// See onDataSetChanged() in ListWidgetService.java, where the slow query is located.\n\t\tmExecutor.execute(() -> {\n\t\t\tfor (int widgetId : appWidgetIds) { // Only update widgets that exist\n\t\t\t\tfinal WidgetPrefs prefs = new WidgetPrefs(context, widgetId);\n\t\t\t\tif (prefs.isPresent()) {\n\t\t\t\t\t// Tell the widgets that the list items should be invalidated and refreshed!\n\t\t\t\t\t// Will call onDatasetChanged in ListWidgetService, doing a new requery\n\t\t\t\t\tappWidgetManager.notifyAppWidgetViewDataChanged(widgetId, R.id.notesList);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/NnnApp.java",
    "content": "package com.nononsenseapps.notepad;\n\nimport android.app.Application;\nimport android.os.Build;\nimport android.os.StrictMode;\n\nimport androidx.annotation.RequiresApi;\n\nimport com.google.android.material.color.DynamicColors;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain;\n\n/**\n * Represents this app. The application object is not guaranteed to stay\n * in memory forever, it WILL get killed.\n */\npublic class NnnApp extends Application {\n\n\t/**\n\t * Called when the application is starting, before any other application\n\t * objects have been created. {@link ActivityMain} is a better place to\n\t * put initialization logic\n\t */\n\t@Override\n\tpublic void onCreate() {\n\t\t// enableStrictModeAnalysis();\n\t\tsuper.onCreate();\n\t\t// use dynamic colors for android >= 13\n\t\tDynamicColors.applyToActivitiesIfAvailable(this);\n\t}\n\n\t/**\n\t * Detects every disk read/write operation, and every time a cursor is not closed.\n\t * Useful for tests during development. Remember that disk activity is core app\n\t * functionality!\n\t */\n\t@RequiresApi(api = Build.VERSION_CODES.P)\n\tprivate static void enableStrictModeAnalysis() {\n\t\tStrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()\n\t\t\t\t.detectDiskReads()\n\t\t\t\t.detectDiskWrites()\n\t\t\t\t.detectNetwork()\n\t\t\t\t.detectAll()\n\t\t\t\t.penaltyLog()\n\t\t\t\t.penaltyFlashScreen()\n\t\t\t\t.build());\n\t\tStrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()\n\t\t\t\t.detectLeakedSqlLiteObjects()\n\t\t\t\t.detectLeakedClosableObjects()\n\t\t\t\t.detectNonSdkApiUsage()\n\t\t\t\t.detectAll()\n\t\t\t\t.penaltyLog()\n\t\t\t\t.build());\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/NotePadBroadcastReceiver.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.provider.BaseColumns;\n\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.widget.list.ListWidgetProvider;\n\n/**\n * Used by {@link ListWidgetProvider} to receive the signal\n * that a note was completed in the widget\n */\npublic class NotePadBroadcastReceiver extends BroadcastReceiver {\n\n\t// TODO but at this point can't you just embed this in ListWidgetProvider.onReceive() ?\n\n\t// if you edit these, see also AndroidManifest.xml\n\tpublic static final String SET_NOTE_COMPLETE = \"com.nononsenseapps.SetNoteComplete\";\n\tpublic static final String SET_NOTE_INCOMPLETE = \"com.nononsenseapps.SetNoteIncomplete\";\n\n\t@Override\n\tpublic void onReceive(final Context context, final Intent intent) {\n\t\tBundle extras = intent.getExtras();\n\t\tif (extras == null || context == null) return;\n\n\t\tlong id = extras.getLong(BaseColumns._ID, -1);\n\t\tif (id <= 0) return;\n\n\t\tString action = intent.getAction();\n\t\tswitch (action) {\n\t\t\tcase SET_NOTE_COMPLETE -> {\n\t\t\t\tTask.setCompleted(context, true, id);\n\t\t\t\t// Toast.makeText(context, R.string.completed, Toast.LENGTH_SHORT).show();\n\t\t\t\t// Broadcast that it has been completed, primarily for AndroidAgendaWidget\n\t\t\t\tIntent i = new Intent(context.getString(R.string.note_completed_broadcast_intent));\n\t\t\t\tcontext.sendBroadcast(i);\n\t\t\t}\n\t\t\tcase SET_NOTE_INCOMPLETE -> Task.setCompleted(context, false, id);\n\t\t\tdefault -> {\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/ActivitySearch.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.activities;\n\nimport android.app.SearchManager;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.MenuItem;\n\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.fragment.app.Fragment;\n\nimport com.nononsenseapps.helpers.ActivityHelper;\nimport com.nononsenseapps.helpers.ThemeHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\nimport com.nononsenseapps.notepad.databinding.FullscreenFragmentBinding;\nimport com.nononsenseapps.notepad.fragments.FragmentSearch;\n\n\npublic class ActivitySearch extends AppCompatActivity {\n\n\tprotected String mQuery = \"\";\n\n\t/**\n\t * for {@link R.layout#fullscreen_fragment}\n\t */\n\tprivate FullscreenFragmentBinding mBinding;\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\t// Must do this before super.onCreate\n\t\tThemeHelper.setTheme(this);\n\t\tActivityHelper.setSelectedLanguage(this);\n\t\tsuper.onCreate(savedInstanceState);\n\t\tmBinding = FullscreenFragmentBinding.inflate(getLayoutInflater());\n\t\tsetContentView(mBinding.getRoot());\n\t\tloadContent();\n\n\t\tgetSupportActionBar().setDisplayShowTitleEnabled(true);\n\t\tgetSupportActionBar().setDisplayHomeAsUpEnabled(true);\n\n\t\thandleIntent(getIntent());\n\t}\n\n\t/**\n\t * To allow child classes to override content\n\t */\n\tprotected Fragment getFragment() {\n\t\treturn FragmentSearch.getInstance(mQuery);\n\t}\n\n\t/**\n\t * Shows the {@link FragmentSearch} with the results\n\t */\n\tvoid loadContent() {\n\t\tgetSupportFragmentManager()\n\t\t\t\t.beginTransaction()\n\t\t\t\t.replace(R.id.fragmentPlaceHolder, getFragment())\n\t\t\t\t.commit();\n\t}\n\n\t@Override\n\tprotected void onNewIntent(Intent intent) {\n\t\tsuper.onNewIntent(intent);\n\t\tsetIntent(intent);\n\t\thandleIntent(intent);\n\t}\n\n\tvoid handleIntent(Intent intent) {\n\t\tif (intent == null) return;\n\n\t\tif (Intent.ACTION_SEARCH.equals(intent.getAction())) {\n\t\t\t// as a result of voice search in the main activity\n\t\t\tmQuery = intent.getStringExtra(SearchManager.QUERY);\n\n\t\t\tvar searchViewMenuItem = (SearchView) this.findViewById(R.id.menu_search);\n\t\t\tif (searchViewMenuItem == null) {\n\t\t\t\t// the search activity did not load yet (for example, a voice search is opening\n\t\t\t\t// ActivitySearch from ActivityMain). In this case, you need to load the content\n\t\t\t\tloadContent();\n\t\t\t} else {\n\t\t\t\t// there is a searchview in this activity. You MUST NOT re-create the fragment.\n\t\t\t\t// Instead, update the query text, and the fragment will show the results\n\t\t\t\tsearchViewMenuItem.setQuery(mQuery, false);\n\t\t\t}\n\t\t} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {\n\t\t\t// when you click a note from the search suggestion in the main activity\n\t\t\tintent.setClass(getApplicationContext(), ActivityMain_.class);\n\t\t\tstartActivity(intent);\n\t\t\tfinish();\n\t\t} else if (intent.getAction() == null) {\n\t\t\t// the archive view was launched from ActivityMain\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tif (item.getItemId() == android.R.id.home) {\n\t\t\tfinish();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/ActivitySearchDeleted.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.activities;\n\nimport androidx.fragment.app.Fragment;\n\nimport com.nononsenseapps.notepad.fragments.FragmentSearchDeleted;\n\n\npublic class ActivitySearchDeleted extends ActivitySearch {\n\n\t@Override\n\tprotected Fragment getFragment() {\n\t\treturn FragmentSearchDeleted.getInstance(mQuery);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/ActivityTaskHistory.java",
    "content": "package com.nononsenseapps.notepad.activities;\n\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.SeekBar;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\n\nimport com.nononsenseapps.helpers.ActivityHelper;\nimport com.nononsenseapps.helpers.ThemeHelper;\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.databinding.ActivityTaskHistoryBinding;\nimport com.nononsenseapps.notepad.fragments.TaskDetailFragment;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\n/**\n * shows a history of all the (saved) previous versions of a note.\n * Open it from {@link TaskDetailFragment }\n */\npublic class ActivityTaskHistory extends AppCompatActivity {\n\n\tpublic static final String RESULT_TEXT_KEY = \"task_text_key\";\n\tprivate long mTaskID;\n\tprivate boolean loaded = false;\n\tprivate Cursor mCursor;\n\n\tprivate SimpleDateFormat timeFormatter;\n\tprivate SimpleDateFormat dbTimeParser;\n\n\t/**\n\t * for {@link R.layout#activity_task_history}\n\t */\n\tprivate ActivityTaskHistoryBinding mBinding;\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\t// Must do this before super.onCreate\n\t\tThemeHelper.setTheme(this);\n\t\tActivityHelper.setSelectedLanguage(this);\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\tmBinding = ActivityTaskHistoryBinding.inflate(getLayoutInflater());\n\t\tsetContentView(mBinding.getRoot());\n\t\tmBinding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\n\t\t\t@Override\n\t\t\tpublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n\t\t\t\tonSeekBarChanged(progress);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onStartTrackingTouch(SeekBar seekBar) {}\n\n\t\t\t@Override\n\t\t\tpublic void onStopTrackingTouch(SeekBar seekBar) {}\n\t\t});\n\n\t\t// Intent must contain a task id\n\t\tif (getIntent() == null || getIntent().getLongExtra(Task.Columns._ID, -1) < 1) {\n\t\t\tsetResult(RESULT_CANCELED, new Intent());\n\t\t\tfinish();\n\t\t\treturn;\n\t\t} else {\n\t\t\tmTaskID = getIntent().getLongExtra(Task.Columns._ID, -1);\n\t\t}\n\n\t\ttimeFormatter = TimeFormatter.getLocalFormatterLong(this);\n\t\t// Default datetime format in sqlite. Set to UTC timezone\n\t\tdbTimeParser = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\", Locale.US);\n\t\tdbTimeParser.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\n\t\tloadActionBarLayout();\n\t}\n\n\tvoid loadActionBarLayout() {\n\t\t// Prepare for failure\n\t\tsetResult(RESULT_CANCELED, new Intent());\n\t\t// Inflate a \"Done/Discard\" custom action bar view.\n\t\tLayoutInflater inflater = (LayoutInflater) getSupportActionBar()\n\t\t\t\t.getThemedContext()\n\t\t\t\t.getSystemService(LAYOUT_INFLATER_SERVICE);\n\t\t@SuppressLint(\"InflateParams\") final View customActionBarView = inflater\n\t\t\t\t.inflate(R.layout.actionbar_custom_view_done_discard, null);\n\t\tcustomActionBarView\n\t\t\t\t.findViewById(R.id.actionbar_done)\n\t\t\t\t.setOnClickListener(v -> {\n\t\t\t\t\t// \"Done\"\n\t\t\t\t\tString txt = mBinding.taskText.getText().toString();\n\t\t\t\t\tfinal Intent returnIntent = new Intent();\n\t\t\t\t\treturnIntent.putExtra(RESULT_TEXT_KEY, txt);\n\t\t\t\t\tsetResult(RESULT_OK, returnIntent);\n\t\t\t\t\tfinish();\n\t\t\t\t});\n\t\tcustomActionBarView\n\t\t\t\t.findViewById(R.id.actionbar_discard)\n\t\t\t\t// \"Cancel result already set\"\n\t\t\t\t.setOnClickListener(v -> finish());\n\n\t\t// Show the custom action bar view and hide the normal Home icon and title.\n\t\tgetSupportActionBar()\n\t\t\t\t.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,\n\t\t\t\t\t\tActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME\n\t\t\t\t\t\t\t\t| ActionBar.DISPLAY_SHOW_TITLE);\n\t\tgetSupportActionBar()\n\t\t\t\t.setCustomView(customActionBarView, new ActionBar.LayoutParams(\n\t\t\t\t\t\tViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n\t}\n\n\t@Override\n\tpublic void onStart() {\n\t\tsuper.onStart();\n\t\tLoaderManager.getInstance(this).restartLoader(0, null,\n\t\t\t\tnew LoaderCallbacks<Cursor>() {\n\n\t\t\t\t\t@NonNull\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {\n\t\t\t\t\t\treturn new CursorLoader(ActivityTaskHistory.this,\n\t\t\t\t\t\t\t\tTask.URI_TASK_HISTORY, Task.Columns.HISTORY_COLUMNS_UPDATED,\n\t\t\t\t\t\t\t\tTask.Columns.HIST_TASK_ID + \" IS ?\",\n\t\t\t\t\t\t\t\tnew String[] { Long.toString(mTaskID) }, null);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> arg0, Cursor c) {\n\t\t\t\t\t\tmCursor = c;\n\t\t\t\t\t\tsetSeekBarProperties();\n\t\t\t\t\t\tif (!loaded) {\n\t\t\t\t\t\t\tmBinding.seekBar.setProgress(c.getCount() - 1);\n\t\t\t\t\t\t\tloaded = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> arg0) {\n\t\t\t\t\t\tmCursor = null;\n\t\t\t\t\t\tsetSeekBarProperties();\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tvoid onSeekBarChanged(int progress) {\n\t\tif (mCursor == null) return;\n\n\t\tif (progress < mCursor.getCount()) {\n\t\t\tmCursor.moveToPosition(progress);\n\t\t\tmBinding.taskText.setTextTitle(mCursor.getString(1));\n\t\t\tmBinding.taskText.setTextRest(mCursor.getString(2));\n\t\t\ttry {\n\t\t\t\tDate x = dbTimeParser.parse(mCursor.getString(3));\n\t\t\t\tmBinding.timestamp.setText(timeFormatter.format(x));\n\t\t\t} catch (ParseException e) {\n\t\t\t\tLog.d(\"nononsenseapps time\", e.getLocalizedMessage());\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid setSeekBarProperties() {\n\t\tif (mCursor == null) {\n\t\t\tmBinding.seekBar.setEnabled(false);\n\t\t\tmBinding.seekBar.setMax(0);\n\t\t} else {\n\t\t\tmBinding.seekBar.setEnabled(true);\n\t\t\tmBinding.seekBar.setMax(mCursor.getCount() - 1);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean onCreateOptionsMenu(Menu menu) {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/main/ActivityMain.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.activities.main;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.content.res.Configuration;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.ActionBarDrawerToggle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.core.view.GravityCompat;\nimport androidx.drawerlayout.widget.DrawerLayout;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.preference.PreferenceManager;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.nononsenseapps.helpers.ActivityHelper;\nimport com.nononsenseapps.helpers.ListHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.NotificationHelper;\nimport com.nononsenseapps.helpers.PermissionsHelper;\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.helpers.SyncStatusMonitor;\nimport com.nononsenseapps.helpers.SyncStatusMonitor.OnSyncStartStopListener;\nimport com.nononsenseapps.helpers.ThemeHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.ActivityMainBinding;\nimport com.nononsenseapps.notepad.fragments.DialogEditList;\nimport com.nononsenseapps.notepad.fragments.TaskDetailFragment;\nimport com.nononsenseapps.notepad.fragments.TaskDetailFragment_;\nimport com.nononsenseapps.notepad.fragments.TaskListFragment;\nimport com.nononsenseapps.notepad.fragments.TaskListViewPagerFragment;\nimport com.nononsenseapps.notepad.interfaces.ListOpener;\nimport com.nononsenseapps.notepad.interfaces.MenuStateController;\nimport com.nononsenseapps.notepad.interfaces.OnFragmentInteractionListener;\nimport com.nononsenseapps.notepad.prefs.AppearancePrefs;\nimport com.nononsenseapps.notepad.prefs.PrefsActivity;\nimport com.nononsenseapps.notepad.sync.orgsync.BackgroundSyncScheduler;\nimport com.nononsenseapps.notepad.sync.orgsync.OrgSyncService;\nimport com.nononsenseapps.ui.ExtraTypesCursorAdapter;\n\nimport org.androidannotations.annotations.AfterViews;\nimport org.androidannotations.annotations.EActivity;\nimport org.androidannotations.annotations.InstanceState;\nimport org.androidannotations.annotations.UiThread;\nimport org.androidannotations.annotations.UiThread.Propagation;\nimport org.androidannotations.annotations.ViewById;\n\nimport java.util.ArrayList;\nimport java.util.concurrent.Executors;\n\n/**\n * This is extended by {@link ActivityMain_}. It was renamed to ActivityList\n * in release 6.0.0 beta, it has to do with getting rid of the annotations\n * library that generates {@link ActivityMain_}\n */\n@EActivity(R.layout.activity_main)\npublic class ActivityMain extends AppCompatActivity\n\t\timplements OnFragmentInteractionListener, OnSyncStartStopListener,\n\t\tMenuStateController, OnSharedPreferenceChangeListener {\n\n\t// Set to true in bundle if exits should be animated\n\tpublic static final String ANIMATEEXIT = \"animateexit\";\n\t// Using tags for test\n\tpublic static final String DETAILTAG = \"detailfragment\";\n\tpublic static final String LISTPAGERTAG = \"listpagerfragment\";\n\n\t@ViewById(resName = \"leftDrawer\")\n\tListView leftDrawer;\n\n\t@ViewById(resName = \"drawerLayout\")\n\tDrawerLayout drawerLayout;\n\n\t@ViewById(resName = \"fragment1\")\n\tView fragment1;\n\n\t// Only present on tablets\n\t@ViewById(resName = \"fragment2\")\n\tView fragment2;\n\n\t// Shown on tablets on start up. Hide on selection\n\t@ViewById(resName = \"taskHint\")\n\tView taskHint;\n\n\t/**\n\t * Which slide animations the {@link TaskDetailFragment} should use for enter and exit\n\t */\n\tboolean mReverseAnimation = false;\n\n\tboolean mAnimateExit = false;\n\n\t/**\n\t * If transitions in this activity should be animated.\n\t * The value is regularly updated by {@link ActivityMain#onResume()}\n\t */\n\tboolean mShouldAnimate = true;\n\n\t/**\n\t * Changes depending on what we're showing since the started activity can receive new intents\n\t */\n\t@InstanceState\n\tboolean isShowingEditor = false;\n\n\tboolean isDrawerClosed = true;\n\tSyncStatusMonitor syncStatusReceiver = null;\n\n\t// WIll only be the viewpager fragment\n\tListOpener listOpener = null;\n\n\t/**\n\t * Helper component that ties the action bar to the navigation drawer.\n\t */\n\tprivate ActionBarDrawerToggle mDrawerToggle;\n\n\t// Only not if opening note directly\n\tprivate boolean shouldAddToBackStack = true;\n\n\tprivate Bundle state;\n\tprivate boolean shouldRestart = false;\n\n\t/**\n\t * for both {@link R.layout#activity_main}\n\t */\n\tprivate ActivityMainBinding mBinding;\n\n\t/**\n\t * called when you rotate the screen, for example. With this, the {@link ActivityMain} can\n\t * remember if the task detail view was showing before. Better than\n\t * {@link AppCompatActivity#onRestoreInstanceState(Bundle)} because this runs before\n\t * {@link #onCreate(Bundle)}, and it's important.\n\t */\n\tprivate void restoreSavedInstanceState_(Bundle savedInstanceState) {\n\t\tif (savedInstanceState == null) return;\n\t\tisShowingEditor = savedInstanceState.getBoolean(\"isShowingEditor\");\n\t}\n\n\t/*\n\t\t@Override\n\t\tpublic void onSaveInstanceState(@NonNull Bundle bundle_) {\n\t\t\tsuper.onSaveInstanceState(bundle_);\n\t\t\tbundle_.putBoolean(\"isShowingEditor\", isShowingEditor);\n\t\t}\n\t*/\n\t@Override\n\tprotected void onPostCreate(Bundle savedInstanceState) {\n\t\tsuper.onPostCreate(savedInstanceState);\n\t\t// Sync the toggle state after onRestoreInstanceState has occurred.\n\t\tif (mDrawerToggle != null) mDrawerToggle.syncState();\n\t}\n\n\t@Override\n\tpublic boolean onCreateOptionsMenu(Menu menu) {\n\t\tsuper.onCreateOptionsMenu(menu);\n\t\tgetMenuInflater().inflate(R.menu.activity_main, menu);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Need a reference to close it when pressing the back button\n\t */\n\tMenuItem mSearchViewMenuItem;\n\n\t@Override\n\tpublic boolean onPrepareOptionsMenu(Menu menu) {\n\t\tmenu.setGroupVisible(R.id.activity_menu_group, isDrawerClosed);\n\t\tmenu.setGroupVisible(R.id.activity_reverse_menu_group, !isDrawerClosed);\n\n\t\t// save a reference so we can close it when pressing the back button\n\t\tmSearchViewMenuItem = menu.findItem(R.id.menu_search);\n\n\t\treturn super.onPrepareOptionsMenu(menu);\n\t}\n\n\t@Override\n\tpublic boolean onOptionsItemSelected(@NonNull MenuItem item) {\n\t\t// Pass the event to ActionBarDrawerToggle. If it returns true, then it has handled the\n\t\t// drawer icon touch event\n\t\tif (mDrawerToggle.onOptionsItemSelected(item)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Handle your other action bar items...\n\t\tint itemId = item.getItemId();\n\t\tif (itemId == android.R.id.home) {\n\t\t\t// the <- arrow was pressed, maybe from the \"task detail\" page\n\t\t\tif (isShowingEditor) {\n\t\t\t\t// Only true in portrait mode\n\t\t\t\tfinal View focusView = ActivityMain.this.getCurrentFocus();\n\t\t\t\tInputMethodManager inputManager = this.getSystemService(InputMethodManager.class);\n\t\t\t\tif (inputManager != null && focusView != null) {\n\t\t\t\t\t// hide soft keyboard\n\t\t\t\t\tinputManager.hideSoftInputFromWindow(focusView.getWindowToken(),\n\t\t\t\t\t\t\tInputMethodManager.HIDE_NOT_ALWAYS);\n\t\t\t\t}\n\n\t\t\t\t// Should load the same list again\n\t\t\t\t// Try getting the list from the original intent\n\t\t\t\tfinal long listId = ActivityMainHelper.getListId(getIntent());\n\n\t\t\t\tfinal Intent intent = new Intent()\n\t\t\t\t\t\t.setAction(Intent.ACTION_VIEW)\n\t\t\t\t\t\t.setClass(ActivityMain.this, ActivityMain_.class);\n\t\t\t\tif (listId > 0) {\n\t\t\t\t\tintent.setData(TaskList.getUri(listId));\n\t\t\t\t}\n\n\t\t\t\t// Set the intent before, so we set the correct action bar\n\t\t\t\tsetIntent(intent);\n\t\t\t\twhile (getSupportFragmentManager().popBackStackImmediate()) {\n\t\t\t\t\t// Need to pop the entire stack and then load\n\t\t\t\t}\n\n\t\t\t\tmReverseAnimation = true;\n\t\t\t\tintent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);\n\t\t\t\tstartActivity(intent);\n\t\t\t} else {\n\t\t\t\t// Handled by the drawer\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.drawer_menu_createlist) {\n\t\t\t// Show fragment\n\t\t\tDialogEditList dialog = DialogEditList.getInstance();\n\t\t\tdialog.setListener(this::openList);\n\t\t\tdialog.show(getSupportFragmentManager(), \"fragment_create_list\");\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_preferences) {\n\t\t\tIntent intent = new Intent();\n\t\t\tintent.setClass(this, PrefsActivity.class);\n\t\t\tstartActivity(intent);\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_sync) {\n\t\t\thandleSyncRequest();\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_delete) {\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void finish() {\n\t\tsuper.finish();\n\t\t// Only animate when specified. Should be when it was animated \"in\"\n\t\tif (mAnimateExit && mShouldAnimate) {\n\t\t\toverridePendingTransition(R.anim.activity_slide_in_right,\n\t\t\t\t\tR.anim.activity_slide_out_right_full);\n\t\t}\n\t}\n\n\t/**\n\t * Opens the specified list and closes the left drawer\n\t */\n\tvoid openList(final long id) {\n\t\t// Open list\n\t\tIntent i = new Intent(ActivityMain.this, ActivityMain_.class)\n\t\t\t\t.setAction(Intent.ACTION_VIEW)\n\t\t\t\t.setData(TaskList.getUri(id))\n\t\t\t\t.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);\n\n\t\t// If editor is on screen, we need to reload fragments\n\t\tif (listOpener == null) {\n\t\t\twhile (getSupportFragmentManager().popBackStackImmediate()) {\n\t\t\t\t// Need to pop the entire stack and then load\n\t\t\t}\n\t\t\tmReverseAnimation = true;\n\t\t\tstartActivity(i);\n\t\t} else {\n\t\t\t// If not popped, then send the call to the fragment\n\t\t\t// directly\n\t\t\tNnnLogger.debug(ActivityMain.class, \"calling listOpener\");\n\t\t\tlistOpener.openList(id);\n\t\t}\n\n\t\t// And then close drawer\n\t\tif (drawerLayout != null && leftDrawer != null) {\n\t\t\tdrawerLayout.closeDrawer(leftDrawer, mShouldAnimate);\n\t\t}\n\t}\n\n\tprivate void handleSyncRequest() {\n\t\tif (!PreferencesHelper.isSincEnabledAtAll(this)) {\n\t\t\tToast.makeText(this, R.string.no_sync_method_chosen,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\tsetRefreshOfAllSwipeLayoutsTo(false);\n\t\t\treturn;\n\t\t}\n\n\t\tboolean syncing = false;\n\n\t\tif (OrgSyncService.areAnyEnabled(this)) {\n\t\t\tsyncing = true;\n\t\t\tOrgSyncService.start(this);\n\t\t}\n\n\t\tif (syncing) {\n\t\t\t// In case of connectivity problems, stop the progress bar\n\t\t\tHandler handler = new Handler(Looper.getMainLooper());\n\t\t\tExecutors.newSingleThreadExecutor().execute(() -> {\n\t\t\t\t// Background work here\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(30 * 1000);\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\tNnnLogger.exception(e);\n\t\t\t\t}\n\n\t\t\t\thandler.post(() -> {\n\t\t\t\t\t// UI Thread work here\n\n\t\t\t\t\t// Notify that the refresh has finished\n\t\t\t\t\tsetRefreshOfAllSwipeLayoutsTo(false);\n\t\t\t\t});\n\t\t\t});\n\t\t} else {\n\t\t\t// explain to the user why the swipe-refresh was canceled\n\t\t\tToast.makeText(this, R.string.no_sync_method_chosen,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\tsetRefreshOfAllSwipeLayoutsTo(false);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onConfigurationChanged(@NonNull Configuration newConfig) {\n\t\tsuper.onConfigurationChanged(newConfig);\n\t\tif (mDrawerToggle != null) mDrawerToggle.onConfigurationChanged(newConfig);\n\t}\n\n\t@Override\n\tpublic void onCreate(Bundle b) {\n\t\t// Must do this before super.onCreate\n\t\tThemeHelper.setTheme(this);\n\t\tActivityHelper.setSelectedLanguage(this);\n\t\tsuper.onCreate(b);\n\t\t/*\n\t\tView bindings don't work here, you must keep android annotations\n\t\trestoreSavedInstanceState_(b);\n\t\tsuper.onCreate(b);\n\t\tmBinding = ActivityMainBinding.inflate(getLayoutInflater());\n\t\tsetContentView(mBinding.getRoot());\n\t\tloadContent();\n\t\t */\n\n\t\tsyncStatusReceiver = new SyncStatusMonitor();\n\n\t\t// First load, then don't add to backstack\n\t\tshouldAddToBackStack = false;\n\n\t\t// To know if we should animate exits\n\t\tif (getIntent() != null && getIntent().getBooleanExtra(ANIMATEEXIT, false)) {\n\t\t\tmAnimateExit = true;\n\t\t}\n\n\t\t// To listen on fragment changes\n\t\tgetSupportFragmentManager().addOnBackStackChangedListener(() -> {\n\t\t\t\t\tif (isShowingEditor && !ActivityMainHelper.isNoteIntent(getIntent())) {\n\t\t\t\t\t\tsetHomeAsDrawer(true);\n\t\t\t\t\t}\n\t\t\t\t\t// Always update menu\n\t\t\t\t\tinvalidateOptionsMenu();\n\t\t\t\t}\n\t\t);\n\n\t\tif (b != null) {\n\t\t\tNnnLogger.debug(ActivityMain.class, \"Activity Saved not null: \" + b);\n\t\t\tthis.state = b;\n\t\t}\n\n\t\t// Setup FAB. TODO are we going to add one ?\n//\t\t mFab = (FloatingActionButton) findViewById(R.id.fab);\n//\t\t mFab.setOnClickListener(view -> {\n//\t\t\t //addTaskInList(\"\", ListHelper.getARealList(this, id_of_the_list));\n//\t\t });\n\n\t\t// Clear possible notifications, schedule future ones\n\t\tfinal Intent intent = getIntent();\n\t\t// Clear notification if present\n\t\tNotificationHelper.clearNotification(this, intent);\n\t\t// Schedule notifications\n\t\tNotificationHelper.schedule(this);\n\t\t// Schedule syncs\n\t\tBackgroundSyncScheduler.scheduleSync(this);\n\t\t// Sync if appropriate\n\t\tOrgSyncService.start(this);\n\n\t\t// Android 13 enforces a more complicated way to call the back button handler\n\t\tgetOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {\n\t\t\t@Override\n\t\t\tpublic void handleOnBackPressed() {\n\t\t\t\tnewOnBackPressed();\n\t\t\t}\n\t\t});\n\n\t\t// keep showing the popup to ask for notification permissions on startup.\n\t\t// The callback function doesn't matter. Android will stop showing it if\n\t\t// the user denies the permission twice.\n\t\tif (!PermissionsHelper.hasPermissions(this, PermissionsHelper.FOR_NOTIFICATIONS))\n\t\t\tthis.requestPermissions(PermissionsHelper.FOR_NOTIFICATIONS,\n\t\t\t\t\tPermissionsHelper.REQCODE_NOTIFICATIONS);\n\n\t\tSharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);\n\t\tprefs.registerOnSharedPreferenceChangeListener(this);\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\t// avoid crashes due to drawer's resources being called when the activity is closing\n\t\tleftDrawer.setAdapter(null);\n\n\t\tsuper.onDestroy();\n\t\tOrgSyncService.stop(this);\n\t}\n\n\t/**\n\t * Updated for android 13+\n\t */\n\tvoid newOnBackPressed() {\n\t\tif (drawerLayout.isDrawerOpen(leftDrawer)) {\n\t\t\t// close the drawer on the left if it's open\n\t\t\tdrawerLayout.closeDrawer(leftDrawer, mShouldAnimate);\n\t\t\treturn;\n\t\t}\n\n\t\t// If search view is expanded, collapse it instead of closing the activity\n\t\tif (mSearchViewMenuItem != null && mSearchViewMenuItem.isActionViewExpanded()) {\n\t\t\tmSearchViewMenuItem.collapseActionView();\n\t\t\tinvalidateOptionsMenu();\n\t\t\treturn;\n\t\t}\n\n\t\t// Reset intent so we get proper fragment handling when the stack pops\n\t\tif (getSupportFragmentManager().getBackStackEntryCount() <= 1) {\n\t\t\t// if you remove this, it shows the wrong icons in the actionbar\n\t\t\t// when you navigate back from TaskDetailView\n\t\t\tsetIntent(new Intent(this, ActivityMain_.class));\n\t\t}\n\n\t\t// replicate super.onBackPressed() behavior\n\t\tif (getSupportFragmentManager().getBackStackEntryCount() > 0) {\n\t\t\tgetSupportFragmentManager().popBackStack();\n\t\t} else {\n\t\t\tfinish();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onPause() {\n\t\tsuper.onPause();\n\t\t// deactivate monitor\n\t\tif (syncStatusReceiver != null) {\n\t\t\tsyncStatusReceiver.stopMonitoring();\n\t\t}\n\t\t// deactivate any progress bar\n\t\tsetRefreshOfAllSwipeLayoutsTo(false);\n\t\t// Pause sync monitors\n\t\tOrgSyncService.pause(this);\n\t}\n\n\t@Override\n\tpublic void onNewIntent(Intent intent) {\n\t\tsuper.onNewIntent(intent);\n\t\tsetIntent(intent);\n\t\tloadFragments();\n\t\t// Just to be sure it gets done. Clear notification if present\n\t\tNotificationHelper.clearNotification(this, intent);\n\t}\n\n\t@Override\n\tpublic void onResume() {\n\t\tif (shouldRestart) {\n\t\t\trestartAndRefresh();\n\t\t}\n\t\tsuper.onResume();\n\n\t\t// activate monitor\n\t\tif (syncStatusReceiver != null) {\n\t\t\tsyncStatusReceiver.startMonitoring(this, this);\n\t\t}\n\t\tOrgSyncService.start(this);\n\n\t\tmShouldAnimate = PreferencesHelper.areAnimationsEnabled(this);\n\t}\n\n\t/**\n\t * Restarts the activity using the same intent that started it.\n\t * Disables animations to get a seamless restart.\n\t */\n\tprivate void restartAndRefresh() {\n\t\tshouldRestart = false;\n\t\tIntent intent = getIntent();\n\t\toverridePendingTransition(0, 0);\n\t\tintent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);\n\t\tfinish();\n\t\toverridePendingTransition(0, 0);\n\t\tstartActivity(intent);\n\t}\n\n\t@UiThread(propagation = Propagation.REUSE)\n\tvoid loadFragments() {\n\t\tfinal Intent intent = getIntent();\n\n\t\t// Mandatory\n\t\tFragment left = null;\n\t\tString leftTag = null;\n\t\t// Only if fragment2 is not null\n\t\tFragment right = null;\n\n\t\tif (this.state != null) {\n\t\t\tthis.state = null;\n\t\t\tif (isShowingEditor && fragment2 != null) {\n\t\t\t\t// Should only be true in portrait\n\t\t\t\tisShowingEditor = false;\n\t\t\t}\n\n\t\t\t// Find fragments\n\t\t\t// This is an instance state variable\n\t\t\tif (isShowingEditor) {\n\t\t\t\t// Portrait, with editor, modify action bar\n\t\t\t\tsetHomeAsDrawer(false);\n\t\t\t\t// Done\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Find the listpager\n\t\t\tleft = getSupportFragmentManager().findFragmentByTag(LISTPAGERTAG);\n\t\t\tlistOpener = (ListOpener) left;\n\n\t\t\tif (left != null) {\n\t\t\t\tif (fragment2 == null) return; // Done\n\t\t\t\tright = getSupportFragmentManager().findFragmentByTag(DETAILTAG);\n\t\t\t}\n\n\t\t\tif (left != null && right != null) return; // Done\n\t\t}\n\n\t\t// Load stuff\n\t\tfinal FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\n\t\tif (mShouldAnimate) {\n\t\t\tif (mReverseAnimation) {\n\t\t\t\tmReverseAnimation = false;\n\t\t\t\ttransaction.setCustomAnimations(\n\t\t\t\t\t\tR.anim.slide_in_bottom, R.anim.slide_out_top,\n\t\t\t\t\t\tR.anim.slide_in_top, R.anim.slide_out_bottom);\n\t\t\t} else {\n\t\t\t\ttransaction.setCustomAnimations(\n\t\t\t\t\t\tR.anim.slide_in_top, R.anim.slide_out_bottom,\n\t\t\t\t\t\tR.anim.slide_in_bottom, R.anim.slide_out_top);\n\t\t\t}\n\t\t}\n\n\t\t// If it contains a noteId, load an editor. If also tablet, load the lists\n\t\tif (fragment2 != null) {\n\t\t\tif (ActivityMainHelper.getNoteId(intent) > 0) {\n\t\t\t\tright = TaskDetailFragment_.getInstance(ActivityMainHelper.getNoteId(intent));\n\t\t\t} else if (ActivityMainHelper.isNoteIntent(intent)) {\n\t\t\t\t// some text was shared to this app\n\t\t\t\tright = TaskDetailFragment_.getInstance(\n\t\t\t\t\t\tActivityMainHelper.getNoteShareText(intent),\n\t\t\t\t\t\tActivityMainHelper.getListIdToShow(intent, this));\n\t\t\t}\n\t\t} else if (ActivityMainHelper.isNoteIntent(intent)) {\n\t\t\tisShowingEditor = true;\n\t\t\tlistOpener = null;\n\t\t\tleftTag = DETAILTAG;\n\t\t\tif (ActivityMainHelper.getNoteId(intent) > 0) {\n\t\t\t\tleft = TaskDetailFragment_.getInstance(ActivityMainHelper.getNoteId(intent));\n\t\t\t} else {\n\t\t\t\t// Get a share text (null safe)\n\t\t\t\t// In a list (if specified, or default otherwise)\n\t\t\t\tleft = TaskDetailFragment_.getInstance(\n\t\t\t\t\t\tActivityMainHelper.getNoteShareText(intent),\n\t\t\t\t\t\tListHelper.getARealList(this, ActivityMainHelper.getListId(intent))\n\t\t\t\t);\n\t\t\t}\n\t\t\t// fucking stack\n\t\t\twhile (getSupportFragmentManager().popBackStackImmediate()) {\n\t\t\t\t// Need to pop the entire stack and then load\n\t\t\t}\n\t\t\tif (shouldAddToBackStack) {\n\t\t\t\ttransaction.addToBackStack(null);\n\t\t\t}\n\t\t\tsetHomeAsDrawer(false);\n\t\t}\n\t\t/*\n\t\t * Other case, is a list id or a tablet\n\t\t */\n\t\tif (!ActivityMainHelper.isNoteIntent(intent) || fragment2 != null) {\n\t\t\t// If we're no longer in the editor, reset the action bar\n\t\t\tif (fragment2 == null) {\n\t\t\t\tsetHomeAsDrawer(true);\n\t\t\t}\n\t\t\t// TODO\n\t\t\tisShowingEditor = false;\n\n\t\t\tleft = TaskListViewPagerFragment.getInstance(\n\t\t\t\t\tActivityMainHelper.getListIdToShow(intent, this));\n\t\t\tleftTag = LISTPAGERTAG;\n\t\t\tlistOpener = (ListOpener) left;\n\t\t}\n\n\t\tif (fragment2 != null && right != null) {\n\t\t\ttransaction.replace(R.id.fragment2, right, DETAILTAG);\n\t\t\ttaskHint.setVisibility(View.GONE);\n\t\t}\n\t\ttransaction.replace(R.id.fragment1, left, leftTag);\n\n\t\t// Commit transaction. Allow state loss as workaround for bug\n\t\t// https://code.google.com/p/android/issues/detail?id=19917\n\t\ttransaction.commitAllowingStateLoss();\n\t\t// Next go, always add\n\t\tshouldAddToBackStack = true;\n\t}\n\n\n\tvoid setHomeAsDrawer(final boolean value) {\n\t\tmDrawerToggle.setDrawerIndicatorEnabled(value);\n\t}\n\n\t/**\n\t * Loads the appropriate fragments depending on state and intent.\n\t */\n\t@AfterViews\n\tprotected void loadContent() {\n\t\tloadLeftDrawer();\n\t\tloadFragments();\n\t}\n\n\t/**\n\t * Load a list of lists in the left drawer\n\t */\n\tprotected void loadLeftDrawer() {\n\t\t// TODO very long function. you should move everything related to drawer\n\t\t//  into static methods in ActivityMainHelper.java\n\n\t\t// TODO handle being called repeatably better?\n\t\t// Set a listener on drawer events\n\t\tif (mDrawerToggle == null) {\n\t\t\t// ActionBarDrawerToggle ties together the the proper interactions\n\t\t\t// between the navigation drawer and the action bar app icon.\n\t\t\tmDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout,\n\t\t\t\t\tR.string.navigation_drawer_open, R.string.navigation_drawer_close) {\n\n\n\t\t\t\t/** custom implementation of\n\t\t\t\t * {@link ActionBarDrawerToggle#onOptionsItemSelected(MenuItem)} from AndroidX\n\t\t\t\t * where we can disable sliding animations\n\t\t\t\t */\n\t\t\t\t@Override\n\t\t\t\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\t\t\t\tif (item == null) return false;\n\t\t\t\t\tif (item.getItemId() != android.R.id.home) return false;\n\t\t\t\t\tif (!this.isDrawerIndicatorEnabled()) return false;\n\n\t\t\t\t\tint drawerLockMode = drawerLayout.getDrawerLockMode(GravityCompat.START);\n\t\t\t\t\tif (drawerLayout.isDrawerVisible(GravityCompat.START)\n\t\t\t\t\t\t\t&& (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_OPEN)) {\n\t\t\t\t\t\t// drawer menu is open --> close it\n\t\t\t\t\t\tdrawerLayout.closeDrawer(GravityCompat.START, mShouldAnimate);\n\t\t\t\t\t\t// mandatory callback, to update menu items\n\t\t\t\t\t\tonDrawerClosed(leftDrawer);\n\t\t\t\t\t} else if (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {\n\t\t\t\t\t\t// drawer menu is closed --> open it\n\t\t\t\t\t\tdrawerLayout.openDrawer(GravityCompat.START, mShouldAnimate);\n\t\t\t\t\t\t// mandatory callback, to update menu items\n\t\t\t\t\t\tonDrawerOpened(leftDrawer);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Called when a drawer has settled in a completely closed state.\n\t\t\t\t */\n\t\t\t\t@Override\n\t\t\t\tpublic void onDrawerClosed(View view) {\n\t\t\t\t\tif (getSupportActionBar() != null) {\n\t\t\t\t\t\t// hide 'Notes' (R.string.app_name_short) from the toolbar\n\t\t\t\t\t\tgetSupportActionBar().setDisplayShowTitleEnabled(false);\n\t\t\t\t\t}\n\t\t\t\t\tisDrawerClosed = true;\n\n\t\t\t\t\t// creates call to onPrepareOptionsMenu()\n\t\t\t\t\tinvalidateOptionsMenu();\n\n\t\t\t\t\tsuper.onDrawerClosed(view);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onDrawerOpened(View drawerView) {\n\t\t\t\t\tif (getSupportActionBar() != null) {\n\t\t\t\t\t\t// show title \"all lists\" when drawer is opened\n\t\t\t\t\t\tgetSupportActionBar().setDisplayShowTitleEnabled(true);\n\t\t\t\t\t\tgetSupportActionBar().setTitle(R.string.show_from_all_lists);\n\t\t\t\t\t}\n\t\t\t\t\t// controls which actionbar items are presented to the user\n\t\t\t\t\tisDrawerClosed = false;\n\t\t\t\t\t// creates call to onPrepareOptionsMenu()\n\t\t\t\t\tinvalidateOptionsMenu();\n\n\t\t\t\t\tsuper.onDrawerOpened(drawerView);\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t\t// this controls only the arrow that rotates on the corner\n\t\t\tmDrawerToggle.setDrawerSlideAnimationEnabled(mShouldAnimate);\n\n\t\t\t// Set the drawer toggle as the DrawerListener\n\t\t\tdrawerLayout.setDrawerListener(mDrawerToggle);\n\t\t}\n\n\t\tActionBar supActBar = getSupportActionBar();\n\t\tif (supActBar == null) {\n\t\t\tNnnLogger.error(ActivityMain.class,\n\t\t\t\t\t\"Coding error: actionbar is null. A crash will follow\");\n\t\t} else {\n\t\t\tsupActBar.setDisplayHomeAsUpEnabled(true);\n\t\t\tsupActBar.setHomeButtonEnabled(true);\n\t\t\t// hide 'Notes' (R.string.app_name_short) from the toolbar\n\t\t\tsupActBar.setDisplayShowTitleEnabled(false);\n\t\t}\n\n\t\t// Use extra items. From top to bottom, they are \"TASKS\", \"Overdue\", \"Today\",\n\t\t// \"Next 5 days\", \"Lists\". Note that 2 of those are used as section titles & dividers\n\t\tfinal int[] extraIds = new int[] { -1, TaskListFragment.LIST_ID_OVERDUE,\n\t\t\t\tTaskListFragment.LIST_ID_TODAY, TaskListFragment.LIST_ID_WEEK, -1 };\n\n\t\t// The corresponding names. This is fine for initial conditions\n\t\tfinal int[] extraStrings = new int[] { R.string.tasks,\n\t\t\t\tR.string.date_header_overdue,\n\t\t\t\tR.string.date_header_today,\n\t\t\t\tR.string.next_5_days,\n\t\t\t\tR.string.lists };\n\n\t\t// Use this for real data\n\t\tfinal ArrayList<ArrayList<Object>> extraData = new ArrayList<>();\n\t\t// Task header\n\t\textraData.add(new ArrayList<>());\n\t\textraData.get(0).add(R.string.tasks);\n\t\t// Overdue\n\t\textraData.add(new ArrayList<>());\n\t\textraData.get(1).add(R.string.date_header_overdue);\n\t\t// Today\n\t\textraData.add(new ArrayList<>());\n\t\textraData.get(2).add(R.string.date_header_today);\n\t\t// Week\n\t\textraData.add(new ArrayList<>());\n\t\textraData.get(3).add(R.string.next_5_days);\n\t\t// Lists header\n\t\textraData.add(new ArrayList<>());\n\t\textraData.get(4).add(R.string.lists);\n\n\t\tfinal int[] extraTypes = new int[] { 1, 0, 0, 0, 1 };\n\n\t\tfinal ExtraTypesCursorAdapter adapter = new ExtraTypesCursorAdapter(\n\t\t\t\tthis,\n\t\t\t\tR.layout.simple_listitem,\n\t\t\t\tnull,\n\t\t\t\tnew String[] { TaskList.Columns.TITLE, TaskList.Columns.VIEW_COUNT },\n\t\t\t\tnew int[] { android.R.id.text1, android.R.id.text2 },\n\t\t\t\textraIds, // id -1 for headers, ignore clicks on them\n\t\t\t\textraStrings,\n\t\t\t\textraTypes,\n\t\t\t\tnew int[] { R.layout.drawer_header }\n\t\t);\n\t\tadapter.setExtraData(extraData);\n\n\t\tleftDrawer.setAdapter(adapter);\n\n\t\t// Set click handler (go to list)\n\t\tleftDrawer.setOnItemClickListener((arg0, v, pos, id) -> {\n\t\t\tif (id < -1) {\n\t\t\t\t// Set preference which type was chosen\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(ActivityMain.this)\n\t\t\t\t\t\t.edit()\n\t\t\t\t\t\t.putLong(TaskListFragment.LIST_ALL_ID_PREF_KEY, id)\n\t\t\t\t\t\t.commit();\n\t\t\t}\n\t\t\topenList(id);\n\t\t});\n\n\t\t// set long-click handler (open popup)\n\t\tleftDrawer.setOnItemLongClickListener((arg0, arg1, pos, id) -> {\n\t\t\t// Open dialog to edit list\n\t\t\tif (id > 0) {\n\t\t\t\tDialogEditList dialog = DialogEditList.getInstance(id);\n\t\t\t\tdialog.show(getSupportFragmentManager(), \"fragment_edit_list\");\n\t\t\t\treturn true;\n\t\t\t} else if (id < -1) {\n\t\t\t\t// Set as \"default\"\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(ActivityMain.this)\n\t\t\t\t\t\t.edit()\n\t\t\t\t\t\t.putLong(getString(R.string.pref_defaultstartlist), id)\n\t\t\t\t\t\t.putLong(TaskListFragment.LIST_ALL_ID_PREF_KEY, id)\n\t\t\t\t\t\t.commit();\n\t\t\t\tToast.makeText(ActivityMain.this, R.string.new_default_set,\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t// openList(id);\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\n\t\t// Load count of tasks in each list, to show the number next to the list's name\n\n\t\t// Define the callback handler\n\t\tfinal LoaderCallbacks<Cursor> callbacks =\n\t\t\t\tnew DrawerCursorLoader(this, extraData, adapter);\n\n\t\t// Load actual data\n\t\tLoaderManager\n\t\t\t\t.getInstance(this)\n\t\t\t\t.restartLoader(0, null, callbacks);\n\t\t// special views\n\t\tLoaderManager\n\t\t\t\t.getInstance(this)\n\t\t\t\t.restartLoader(TaskListFragment.LIST_ID_OVERDUE, null, callbacks);\n\t\tLoaderManager\n\t\t\t\t.getInstance(this)\n\t\t\t\t.restartLoader(TaskListFragment.LIST_ID_TODAY, null, callbacks);\n\t\tLoaderManager\n\t\t\t\t.getInstance(this)\n\t\t\t\t.restartLoader(TaskListFragment.LIST_ID_WEEK, null, callbacks);\n\t}\n\n\t@Override\n\tpublic void onFragmentInteraction(final Uri taskUri, final long listId, final View origin) {\n\t\tfinal Intent intent = new Intent()\n\t\t\t\t.setAction(Intent.ACTION_EDIT)\n\t\t\t\t.setClass(this, ActivityMain_.class)\n\t\t\t\t.setData(taskUri)\n\t\t\t\t.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)\n\t\t\t\t.putExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, listId);\n\t\t// User clicked a task in the list\n\t\tif (fragment2 != null) {\n\t\t\t// tablet\n\n\t\t\t// Set the intent here also so rotations open the same item\n\t\t\tsetIntent(intent);\n\t\t\tvar tmp1 = getSupportFragmentManager().beginTransaction();\n\t\t\tif (mShouldAnimate) {\n\t\t\t\ttmp1.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom);\n\t\t\t}\n\t\t\ttmp1.replace(R.id.fragment2, TaskDetailFragment_.getInstance(taskUri))\n\t\t\t\t\t.commitAllowingStateLoss();\n\t\t\ttaskHint.setVisibility(View.GONE);\n\t\t} else {\n\t\t\t// phone\n\t\t\tstartActivity(intent);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addTaskInList(final String text, final long listId) {\n\t\tif (listId < 1) {\n\t\t\t// Cant add to invalid lists\n\t\t\t// Snackbar.make(mFab, \"Please create a list first\", Snackbar.LENGTH_LONG).show();\n\t\t\treturn;\n\t\t}\n\t\tfinal Intent intent = new Intent()\n\t\t\t\t.setAction(Intent.ACTION_INSERT)\n\t\t\t\t.setClass(this, ActivityMain_.class)\n\t\t\t\t.setData(Task.URI)\n\t\t\t\t.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)\n\t\t\t\t.putExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, listId);\n\t\tif (fragment2 != null) {\n\t\t\t// Set intent to preserve state when rotating\n\t\t\tsetIntent(intent);\n\t\t\t// Replace editor fragment\n\t\t\tvar tmp1 = getSupportFragmentManager().beginTransaction();\n\t\t\tif (mShouldAnimate) {\n\t\t\t\ttmp1.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom);\n\t\t\t}\n\t\t\ttmp1.replace(R.id.fragment2, TaskDetailFragment_.getInstance(text, listId), DETAILTAG)\n\t\t\t\t\t.commitAllowingStateLoss();\n\t\t\ttaskHint.setVisibility(View.GONE);\n\t\t} else {\n\t\t\t// Open an activity\n\t\t\tstartActivity(intent);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void closeFragment(final Fragment fragment) {\n\t\tif (fragment2 != null) {\n\t\t\tvar tmp1 = getSupportFragmentManager().beginTransaction();\n\t\t\tif (mShouldAnimate) {\n\t\t\t\ttmp1.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom);\n\t\t\t}\n\t\t\ttmp1.remove(fragment).commitAllowingStateLoss();\n\t\t\ttaskHint.setAlpha(0f);\n\t\t\ttaskHint.setVisibility(View.VISIBLE);\n\t\t\ttaskHint.animate()\n\t\t\t\t\t.alpha(1f)\n\t\t\t\t\t.setStartDelay(500);\n\t\t} else {\n\t\t\t// Phone case, simulate back button\n\t\t\tsimulateBack();\n\t\t}\n\t}\n\n\tprivate void simulateBack() {\n\t\tif (getSupportFragmentManager().getBackStackEntryCount() <= 1) {\n\t\t\tsetIntent(new Intent(this, ActivityMain_.class));\n\t\t}\n\n\t\tif (!getSupportFragmentManager().popBackStackImmediate()) {\n\t\t\tfinish();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean childItemsVisible() {\n\t\treturn isDrawerClosed;\n\t}\n\n\t@Override\n\tpublic void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {\n\t\tif (key == null) {\n\t\t\t// it happens sometimes during Espresso tests\n\t\t\treturn;\n\t\t}\n\t\tif (key.equals(AppearancePrefs.KEY_THEME) || key.equals(getString(R.string.pref_locale))) {\n\t\t\tshouldRestart = true;\n\t\t} else if (key.startsWith(\"pref_restart\")) {\n\t\t\tshouldRestart = true;\n\t\t}\n\t}\n\n\t// holds all the swipe-to-refresh layouts of the various TaskListFragments\n\tprivate final ArrayList<SwipeRefreshLayout> swpRefLayouts = new ArrayList<>();\n\n\t/**\n\t * every {@link TaskListFragment} has its own instance of a {@link SwipeRefreshLayout},\n\t * so here they're all added to a private list. Then, the {@link ActivityMain} will update\n\t * them all when necessary\n\t */\n\tpublic void addSwipeRefreshLayoutToList(SwipeRefreshLayout newSwpRefLayout) {\n\t\t// TODO do this Only if some sync is enabled\n\n\t\t// Show the accent color on the arrow while loading\n\t\tnewSwpRefLayout.setColorSchemeResources(R.color.accent);\n\n\t\t// TODO the swipe-to-refresh layouts have been disabled because they make it impossible\n\t\t//  to manually drag down the 1° note. When you find a solution for this, delete this line:\n\t\tnewSwpRefLayout.setEnabled(false);\n\n\t\t// Sets up a Listener that is invoked when the user performs a swipe-to-refresh gesture.\n\t\tnewSwpRefLayout.setOnRefreshListener(\n\t\t\t\t() -> {\n\t\t\t\t\tLog.i(\"NNN\", \"onRefresh called from SwipeRefreshLayout\");\n\n\t\t\t\t\t// This method performs the actual data-refresh operation.\n\t\t\t\t\t// The method must call setRefreshing(false) when it's finished.\n\t\t\t\t\thandleSyncRequest();\n\t\t\t\t}\n\t\t);\n\t\tswpRefLayouts.add(newSwpRefLayout);\n\t}\n\n\t/**\n\t * sets the refreshing status of all {@link SwipeRefreshLayout} in this activity\n\t *\n\t * @param newState FALSE if they should stop the animation, TRUE if they should show it\n\t */\n\tprivate void setRefreshOfAllSwipeLayoutsTo(boolean newState) {\n\t\tfor (SwipeRefreshLayout layout : swpRefLayouts) {\n\t\t\tlayout.setRefreshing(newState);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onSyncStartStop(final boolean isOngoing) {\n\t\t// Notify PullToRefreshAttacher of the refresh state\n\t\tthis.runOnUiThread(() -> setRefreshOfAllSwipeLayoutsTo(isOngoing));\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/main/ActivityMainHelper.java",
    "content": "package com.nononsenseapps.notepad.activities.main;\n\nimport android.content.Context;\nimport android.content.Intent;\n\nimport androidx.annotation.NonNull;\n\nimport com.nononsenseapps.helpers.ListHelper;\nimport com.nononsenseapps.notepad.database.LegacyDBHelper;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.fragments.TaskDetailFragment;\n/**\n * static methods that take some code away from {@link ActivityMain}. It's used only in that\n * class, that's why it's package-private\n */\nclass ActivityMainHelper {\n\n\t/**\n\t * @param intent from code in {@link ActivityMain}\n\t * @return a list id from an intent if it contains one, either as part of\n\t * its URI or as an extra. Returns -1 if no id was contained, this includes insert actions\n\t */\n\tstatic long getListId(final Intent intent) {\n\t\tlong retval = -1;\n\t\tif (intent != null && intent.getData() != null &&\n\t\t\t\t(Intent.ACTION_EDIT.equals(intent.getAction()) ||\n\t\t\t\t\t\tIntent.ACTION_VIEW.equals(intent.getAction()) ||\n\t\t\t\t\t\tIntent.ACTION_INSERT.equals(intent.getAction()))) {\n\n\t\t\tString path = intent.getData().getPath();\n\t\t\tif ((path.startsWith(LegacyDBHelper.NotePad.Lists.PATH_VISIBLE_LISTS) ||\n\t\t\t\t\tpath.startsWith(LegacyDBHelper.NotePad.Lists.PATH_LISTS) ||\n\t\t\t\t\tpath.startsWith(TaskList.URI.getPath()))) {\n\t\t\t\ttry {\n\t\t\t\t\tretval = Long.parseLong(intent.getData().getLastPathSegment());\n\t\t\t\t} catch (NumberFormatException ignored) {\n\t\t\t\t\t// retval remains = -1\n\t\t\t\t}\n\t\t\t} else if (-1 != intent.getLongExtra(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_LIST, -1)) {\n\t\t\t\tretval = intent.getLongExtra(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_LIST, -1);\n\t\t\t} else if (-1 != intent.getLongExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, -1)) {\n\t\t\t\tretval = intent.getLongExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, -1);\n\t\t\t} else if (-1 != intent.getLongExtra(Task.Columns.DBLIST, -1)) {\n\t\t\t\tretval = intent.getLongExtra(Task.Columns.DBLIST, -1);\n\t\t\t}\n\t\t}\n\t\treturn retval;\n\t}\n\n\t/**\n\t * Returns the text that has been shared with the app. Does not check\n\t * anything other than EXTRA_SUBJECT AND EXTRA_TEXT\n\t * <p/>\n\t * If it is a Google Now intent, will ignore the subject which is\n\t * \"Note to self\"\n\t */\n\tstatic String getNoteShareText(final Intent intent) {\n\t\tif (intent == null || intent.getExtras() == null) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tStringBuilder retval = new StringBuilder();\n\t\t// possible title\n\t\tif (intent.getExtras().containsKey(Intent.EXTRA_SUBJECT) &&\n\t\t\t\t!\"com.google.android.gm.action.AUTO_SEND\".equals(intent.getAction())) {\n\t\t\tretval.append(intent.getExtras().get(Intent.EXTRA_SUBJECT));\n\t\t}\n\t\t// possible note\n\t\tif (intent.getExtras().containsKey(Intent.EXTRA_TEXT)) {\n\t\t\tif (retval.length() > 0) {\n\t\t\t\tretval.append(\"\\n\");\n\t\t\t}\n\t\t\tretval.append(intent.getExtras().get(Intent.EXTRA_TEXT));\n\t\t}\n\t\treturn retval.toString();\n\t}\n\n\t/**\n\t * Returns a note id from an intent if it contains one, either as part of\n\t * its URI or as an extra\n\t * <p/>\n\t * Returns -1 if no id was contained, this includes insert actions\n\t */\n\tstatic long getNoteId(@NonNull final Intent intent) {\n\t\tlong retval = -1;\n\t\tif (intent.getData() != null &&\n\t\t\t\t(Intent.ACTION_EDIT.equals(intent.getAction()) ||\n\t\t\t\t\t\tIntent.ACTION_VIEW.equals(intent.getAction()))) {\n\t\t\tif (intent.getData().getPath().startsWith(TaskList.URI.getPath())) {\n\t\t\t\t// Find it in the extras. See DashClock extension for an example\n\t\t\t\tretval = intent.getLongExtra(Task.TABLE_NAME, -1);\n\t\t\t} else if ((intent.getData().getPath().startsWith(\n\t\t\t\t\tLegacyDBHelper.NotePad.Notes.PATH_VISIBLE_NOTES) ||\n\t\t\t\t\tintent.getData().getPath().startsWith(\n\t\t\t\t\t\t\tLegacyDBHelper.NotePad.Notes.PATH_NOTES) ||\n\t\t\t\t\tintent.getData().getPath()\n\t\t\t\t\t\t\t.startsWith(Task.URI.getPath()))) {\n\t\t\t\tretval = Long.parseLong(intent.getData().getLastPathSegment());\n\t\t\t}\n\t\t}\n\t\treturn retval;\n\t}\n\n\t/**\n\t * Returns true the intent URI targets a note. Either an edit/view or\n\t * insert.\n\t */\n\tstatic boolean isNoteIntent(final Intent intent) {\n\t\tif (intent == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (Intent.ACTION_SEND.equals(intent.getAction()) ||\n\t\t\t\t\"com.google.android.gm.action.AUTO_SEND\"\n\t\t\t\t\t\t.equals(intent.getAction())) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn intent.getData() != null &&\n\t\t\t\t(Intent.ACTION_EDIT.equals(intent.getAction()) ||\n\t\t\t\t\t\tIntent.ACTION_VIEW.equals(intent.getAction()) ||\n\t\t\t\t\t\tIntent.ACTION_INSERT.equals(intent.getAction())) &&\n\t\t\t\t(intent.getData().getPath().startsWith(LegacyDBHelper.NotePad.Notes.PATH_VISIBLE_NOTES) ||\n\t\t\t\t\t\tintent.getData().getPath().startsWith(LegacyDBHelper.NotePad.Notes.PATH_NOTES) ||\n\t\t\t\t\t\tintent.getData().getPath().startsWith(Task.URI.getPath())) &&\n\t\t\t\t!intent.getData().getPath().startsWith(TaskList.URI.getPath());\n\t}\n\n\t/**\n\t * If intent contains a list_id, returns that.\n\t * Else, checks preferences for default list setting.\n\t * Else, -1.\n\t */\n\tstatic long getListIdToShow(final Intent intent, Context context) {\n\t\tlong result = ActivityMainHelper.getListId(intent);\n\t\treturn ListHelper.getAShowList(context, result);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/main/DrawerCursorLoader.java",
    "content": "package com.nononsenseapps.notepad.activities.main;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.fragments.TaskListFragment;\nimport com.nononsenseapps.ui.ExtraTypesCursorAdapter;\n\nimport java.util.ArrayList;\n\n/**\n * Used only in {@link ActivityMain#loadLeftDrawer()}, so it's a package-private class\n */\nclass DrawerCursorLoader implements LoaderManager.LoaderCallbacks<Cursor> {\n\n\tfinal String[] COUNTROWS = new String[] { \"COUNT(1)\" };\n\tfinal String NOTCOMPLETED = Task.Columns.COMPLETED + \" IS NULL \";\n\n\t/**\n\t * the instance of {@link ActivityMain} that hosts this loader object\n\t */\n\tContext mContext;\n\n\t/**\n\t * this and {@link #mAdapter} are references to the variables in\n\t * {@link ActivityMain#loadLeftDrawer()}, so it's exactly as if this code\n\t * was copypasted in that function\n\t */\n\tArrayList<ArrayList<Object>> mExtraData;\n\tExtraTypesCursorAdapter mAdapter;\n\n\tpublic DrawerCursorLoader(ActivityMain drawerHost, ArrayList<ArrayList<Object>> extraData,\n\t\t\t\t\t\t\t  ExtraTypesCursorAdapter adapter) {\n\t\tmContext = drawerHost;\n\t\tmExtraData = extraData;\n\t\tmAdapter = adapter;\n\t}\n\n\t@NonNull\n\t@Override\n\tpublic Loader<Cursor> onCreateLoader(int id, Bundle arg1) {\n\t\t// Normal lists\n\t\treturn switch (id) {\n\t\t\tcase TaskListFragment.LIST_ID_OVERDUE -> new CursorLoader(mContext, Task.URI, COUNTROWS,\n\t\t\t\t\tNOTCOMPLETED + TaskListFragment.andWhereOverdue(),\n\t\t\t\t\tnull, null);\n\t\t\tcase TaskListFragment.LIST_ID_TODAY -> new CursorLoader(mContext, Task.URI, COUNTROWS,\n\t\t\t\t\tNOTCOMPLETED + TaskListFragment.andWhereToday(),\n\t\t\t\t\tnull, null);\n\t\t\tcase TaskListFragment.LIST_ID_WEEK -> new CursorLoader(mContext, Task.URI, COUNTROWS,\n\t\t\t\t\tNOTCOMPLETED + TaskListFragment.andWhereWeek(),\n\t\t\t\t\tnull, null);\n\t\t\tdefault -> new CursorLoader(mContext,\n\t\t\t\t\tTaskList.URI_WITH_COUNT,\n\t\t\t\t\tnew String[] { TaskList.Columns._ID, TaskList.Columns.TITLE,\n\t\t\t\t\t\t\tTaskList.Columns.VIEW_COUNT },\n\t\t\t\t\tnull, null,\n\t\t\t\t\tmContext.getResources()\n\t\t\t\t\t\t\t.getString(R.string.const_as_alphabetic, TaskList.Columns.TITLE));\n\t\t};\n\t}\n\n\t@Override\n\tpublic void onLoadFinished(Loader<Cursor> l, Cursor c) {\n\t\tswitch (l.getId()) {\n\t\t\tcase TaskListFragment.LIST_ID_OVERDUE -> {\n\t\t\t\tif (c.moveToFirst()) {\n\t\t\t\t\tupdateExtra(1, c.getInt(0));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase TaskListFragment.LIST_ID_TODAY -> {\n\t\t\t\tif (c.moveToFirst()) {\n\t\t\t\t\tupdateExtra(2, c.getInt(0));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase TaskListFragment.LIST_ID_WEEK -> {\n\t\t\t\tif (c.moveToFirst()) {\n\t\t\t\t\tupdateExtra(3, c.getInt(0));\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault -> mAdapter.swapCursor(c);\n\t\t}\n\t}\n\n\tprivate void updateExtra(final int pos, final int count) {\n\t\twhile (mExtraData.get(pos).size() < 2) {\n\t\t\t// To avoid crashes\n\t\t\tmExtraData.get(pos).add(\"0\");\n\t\t}\n\t\tmExtraData.get(pos).set(1, Integer.toString(count));\n\t\tmAdapter.notifyDataSetChanged();\n\t}\n\n\t@Override\n\tpublic void onLoaderReset(Loader<Cursor> l) {\n\t\tswitch (l.getId()) {\n\t\t\tcase TaskListFragment.LIST_ID_OVERDUE:\n\t\t\tcase TaskListFragment.LIST_ID_TODAY:\n\t\t\tcase TaskListFragment.LIST_ID_WEEK:\n\t\t\t\tbreak;\n\t\t\tcase 0:\n\t\t\tdefault:\n\t\t\t\tmAdapter.swapCursor(null);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/DummyProvider.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.android.provider;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.net.Uri;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.nononsenseapps.notepad.BuildConfig;\nimport com.nononsenseapps.notepad.providercontract.ProviderContract;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class DummyProvider extends ContentProvider {\n\n\t// TODO change authority and add corresponding manifest entry\n\tpublic static final String AUTHORITY = BuildConfig.APPLICATION_ID + \".DUMMYPROVIDER.AUTHORITY\";\n\tpublic static final String SCHEME = \"content://\";\n\tprivate static final String TAG = \"DummyProvider\";\n\n\tprivate static final String TYPE_NONONSENSENOTES_ITEM = \"vnd.android.cursor.item/item\";\n\n\tprivate List<DummyItem> mData;\n\n\tpublic DummyProvider() {}\n\n\t@Override\n\tpublic boolean onCreate() {\n\t\t// TODO change this to actual initialization code for your own data backend\n\t\tmData = initDummyData();\n\t\treturn true;\n\t}\n\n\t/**\n\t * @return initial dummy data\n\t */\n\tprivate List<DummyItem> initDummyData() {\n\t\tArrayList<DummyItem> items = new ArrayList<>();\n\n\t\t// The uri is simply the position in the array(s)\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tDummyItem top = new DummyItem(\"/\" + i, \"Top item \" + i);\n\t\t\titems.add(top);\n\n\t\t\tfor (int j = 0; j < 3; j++) {\n\t\t\t\tDummyItem sub = new DummyItem(top.getPath() + \"/\" + j, \"Sub item \" + j);\n\t\t\t\ttop.children.add(sub);\n\n\t\t\t\tfor (int k = 0; k < 3; k++) {\n\t\t\t\t\tDummyItem subsub = new DummyItem(\n\t\t\t\t\t\t\tsub.getPath() + \"/\" + k,\n\t\t\t\t\t\t\t\"Subsub item \" + k);\n\t\t\t\t\tsub.children.add(subsub);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t@Override\n\tpublic String getType(@NonNull Uri uri) {\n\t\tswitch (ProviderHelper.matchUri(uri)) {\n\t\t\tcase ProviderHelper.URI_NOMATCH:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown path: \" + uri.getPath());\n\t\t\tdefault:\n\t\t\t\treturn TYPE_NONONSENSENOTES_ITEM;\n\n\t\t}\n\t}\n\n\t@Override\n\tpublic int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {\n\t\t// Implement this to handle requests to delete one or more rows.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\n\t@Override\n\tpublic Uri insert(@NonNull Uri uri, ContentValues values) {\n\t\tString path;\n\t\tswitch (ProviderHelper.matchUri(uri)) {\n\t\t\tcase ProviderHelper.URI_LIST:\n\t\t\t\tpath = ProviderHelper.getRelativePath(uri);\n\n\t\t\t\tfinal String parentPath;\n\t\t\t\tfinal List<DummyItem> list;\n\t\t\t\tif (\"/\".equals(path)) {\n\t\t\t\t\tparentPath = \"/\";\n\t\t\t\t\tlist = mData;\n\t\t\t\t} else {\n\t\t\t\t\tDummyItem parent = getNestedItem(path);\n\t\t\t\t\tparentPath = parent.path;\n\t\t\t\t\tlist = parent.children;\n\t\t\t\t}\n\n\t\t\t\tDummyItem item = new DummyItem(ProviderHelper.join(parentPath, Integer.toString(list.size())),\n\t\t\t\t\t\tvalues);\n\t\t\t\tlist.add(item);\n\n\t\t\t\tnotifyOnChange(uri);\n\t\t\t\treturn ProviderHelper.getDetailsUri(ProviderHelper.getBase(uri), item.path);\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Can't perform insert at: \" + uri);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Cursor query(@NonNull Uri uri, String[] projection, String selection,\n\t\t\t\t\t\tString[] selectionArgs, String sortOrder) {\n\t\tLog.d(TAG, \"Uri: \" + uri);\n\n\t\tString path;\n\t\tMatrixCursor mc = new MatrixCursor(ProviderContract.sMainListProjection);\n\n\t\tswitch (ProviderHelper.matchUri(uri)) {\n\t\t\tcase ProviderHelper.URI_ROOT:\n\t\t\t\tsetNotificationUri(mc, ProviderHelper\n\t\t\t\t\t\t.getListUri(ProviderHelper.getBase(uri), \"\"));\n\t\t\t\tfor (DummyItem item : mData) {\n\t\t\t\t\tmc.addRow(item.asRow());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ProviderHelper.URI_LIST:\n\t\t\t\tsetNotificationUri(mc, uri);\n\t\t\t\tpath = ProviderHelper.getRelativePath(uri);\n\n\t\t\t\tfor (DummyItem item : getNestedList(path)) {\n\t\t\t\t\tmc.addRow(item.asRow());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ProviderHelper.URI_DETAILS:\n\t\t\t\tsetNotificationUri(mc, uri);\n\t\t\t\tpath = ProviderHelper.getRelativePath(uri);\n\t\t\t\tmc.addRow(getNestedItem(path).asRow());\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown path: \" + uri);\n\t\t}\n\n\t\treturn mc;\n\t}\n\n\t/**\n\t * Sets the notifcation uri on the cursor.\n\t */\n\tprotected void setNotificationUri(Cursor c, Uri uri) {\n\t\tContext context = getContext();\n\t\tif (context != null) {\n\t\t\tc.setNotificationUri(context.getContentResolver(), uri);\n\t\t}\n\t}\n\n\t/**\n\t * Walk the tree, decomposing the path as we walk\n\t *\n\t * @param path like /1/2/3/4\n\t * @return the list of children in item /1/2/3/4\n\t */\n\tprivate List<DummyItem> getNestedList(String path) {\n\t\tList<DummyItem> items = mData;\n\t\tString first = ProviderHelper.firstPart(path);\n\t\tpath = ProviderHelper.restPart(path);\n\t\twhile (!first.isEmpty()) {\n\t\t\tint index = Integer.parseInt(first);\n\t\t\titems = items.get(index).children;\n\n\t\t\tfirst = ProviderHelper.firstPart(path);\n\t\t\tpath = ProviderHelper.restPart(path);\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t/**\n\t * Walk the tree, decomposing the path as we walk\n\t *\n\t * @param path like /1/2/3/4\n\t * @return the item /1/2/3/4\n\t */\n\tprivate DummyItem getNestedItem(String path) {\n\t\treturn getNestedItem(path, false);\n\t}\n\n\t/**\n\t * Walk the tree, decomposing the path as we walk\n\t *\n\t * @param path    like /1/2/3/4\n\t * @param popItem true if item should be removed from its parent list also\n\t * @return the item /1/2/3/4\n\t */\n\tprivate DummyItem getNestedItem(String path, boolean popItem) {\n\t\tList<DummyItem> items = mData;\n\t\tDummyItem item = null;\n\t\tString first = ProviderHelper.firstPart(path);\n\t\tpath = ProviderHelper.restPart(path);\n\t\twhile (!first.isEmpty()) {\n\t\t\tint index = Integer.parseInt(first);\n\t\t\titem = items.get(index);\n\t\t\titems = item.children;\n\n\t\t\tfirst = ProviderHelper.firstPart(path);\n\t\t\tpath = ProviderHelper.restPart(path);\n\t\t}\n\n\t\tif (popItem) {\n\t\t\titems.remove(item);\n\t\t}\n\n\t\treturn item;\n\t}\n\n\t@Override\n\tpublic int update(@NonNull Uri uri, ContentValues values, String selection,\n\t\t\t\t\t  String[] selectionArgs) {\n\t\tString path;\n\t\tswitch (ProviderHelper.matchUri(uri)) {\n\t\t\tcase ProviderHelper.URI_DETAILS:\n\t\t\t\tpath = ProviderHelper.getRelativePath(uri);\n\n\t\t\t\t// Any queries?\n\t\t\t\tif (uri.getQuery().isEmpty()) {\n\t\t\t\t\t// Update values\n\t\t\t\t\tgetNestedItem(path).update(values);\n\t\t\t\t} else {\n\t\t\t\t\t// Move query\n\t\t\t\t\tString previous = uri.getQueryParameter(ProviderContract.QUERY_MOVE_PREVIOUS);\n\t\t\t\t\tString parent = uri.getQueryParameter(ProviderContract.QUERY_MOVE_PARENT);\n\n\t\t\t\t\tif (previous != null || parent != null) {\n\t\t\t\t\t\tmoveDummyItem(path, previous, parent);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tnotifyOnChange(uri);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Can't perform insert at: \" + uri);\n\t\t}\n\n\t\t// TODO: Implement this to handle requests to update one or more rows.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t/**\n\t * @param path     relativepath to item to move\n\t * @param previous relativepath to sibling which should be placed before item\n\t * @param parent   relativepath to parent item\n\t */\n\tprivate void moveDummyItem(String path, String previous, String parent) {\n\t\t// Pop the item\n\t\tDummyItem item = getNestedItem(path, true);\n\n\t\tList<DummyItem> parentList;\n\t\tif (parent == null || parent.isEmpty()) {\n\t\t\tparentList = mData;\n\t\t} else {\n\t\t\tparentList = getNestedList(parent);\n\t\t}\n\n\t\tint prevIndex = -1;\n\t\tif (previous != null && !previous.isEmpty()) {\n\t\t\tDummyItem prevItem = getNestedItem(previous);\n\t\t\tprevIndex = parentList.indexOf(prevItem);\n\t\t}\n\n\t\t// Insert into parentList at correct position\n\t\tparentList.add(prevIndex + 1, item);\n\t}\n\n\t/**\n\t * Call this after the data changes\n\t *\n\t * @param uri to notify updates on\n\t */\n\tprotected void notifyOnChange(@NonNull Uri uri) {\n\t\tContext context = getContext();\n\t\tif (context != null) {\n\t\t\tcontext.getContentResolver().notifyChange(uri, null);\n\t\t}\n\t}\n\n\t/**\n\t * Just some helpers item that represent the data backing this provider.\n\t */\n\tprivate static class DummyItem {\n\t\tpublic ArrayList<DummyItem> children = new ArrayList<>();\n\t\tprotected String path;\n\t\tprotected long typemask = 0;\n\t\tprotected String title = \"\";\n\t\tprotected String description = null;\n\t\tprotected String status = null;\n\t\tprotected String due = null;\n\t\tprotected boolean deleted = false;\n\n\t\tpublic DummyItem(@NonNull String path, @NonNull String title) {\n\t\t\tthis(path, title, ProviderContract.getTypeMask(ProviderContract.TYPE_DATA,\n\t\t\t\t\tProviderContract.TYPE_FOLDER));\n\t\t}\n\n\t\tpublic DummyItem(@NonNull String path, @NonNull String title, long bitmask) {\n\t\t\tthis.path = path;\n\t\t\tthis.title = title;\n\t\t\tthis.typemask = bitmask;\n\t\t}\n\n\t\tpublic DummyItem(@NonNull String path, @NonNull ContentValues values) {\n\t\t\tthis.path = path;\n\t\t\tthis.title = values.getAsString(ProviderContract.COLUMN_TITLE);\n\t\t\tthis.typemask = ProviderContract.getTypeMask(ProviderContract.TYPE_DATA,\n\t\t\t\t\tProviderContract.TYPE_FOLDER);\n\t\t}\n\n\t\tpublic Object[] asRow() {\n\t\t\t// For insertion into matrixcursor\n\t\t\treturn new Object[] { path, typemask, title, description, status, due };\n\t\t}\n\n\t\tpublic String getTitle() {\n\t\t\treturn title;\n\t\t}\n\n\t\tpublic void setTitle(@NonNull String title) {\n\t\t\tthis.title = title;\n\t\t}\n\n\t\tpublic long getTypemask() {\n\t\t\treturn typemask;\n\t\t}\n\n\t\tpublic void setTypemask(long typemask) {\n\t\t\tthis.typemask = typemask;\n\t\t}\n\n\t\tpublic String getDescription() {\n\t\t\treturn description;\n\t\t}\n\n\t\tpublic void setDescription(@Nullable String description) {\n\t\t\tthis.description = description;\n\t\t}\n\n\t\tpublic String getStatus() {\n\t\t\treturn status;\n\t\t}\n\n\t\tpublic void setStatus(@Nullable String status) {\n\t\t\tthis.status = status;\n\t\t}\n\n\t\tpublic String getDue() {\n\t\t\treturn due;\n\t\t}\n\n\t\tpublic void setDue(@Nullable String due) {\n\t\t\tthis.due = due;\n\t\t}\n\n\t\tpublic boolean isDeleted() {\n\t\t\treturn deleted;\n\t\t}\n\n\t\tpublic void setDeleted(boolean deleted) {\n\t\t\tthis.deleted = deleted;\n\t\t}\n\n\t\tpublic String getPath() {\n\t\t\treturn path;\n\t\t}\n\n\t\tpublic void setPath(String path) {\n\t\t\tthis.path = path;\n\t\t}\n\n\t\tpublic void update(ContentValues values) {\n\t\t\tthis.title = values.getAsString(ProviderContract.COLUMN_TITLE);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/ProviderHelper.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.android.provider;\n\nimport android.net.Uri;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.Locale;\nimport java.util.Objects;\n\n\n/**\n * Helper functions related to provider operations.\n */\npublic final class ProviderHelper {\n\n\tpublic static final int URI_NOMATCH = -1;\n\tpublic static final int URI_ROOT = 101;\n\tpublic static final int URI_LIST = 102;\n\tpublic static final int URI_DETAILS = 103;\n\tpublic static final String URI_LIST_PREFIX = \"list\";\n\tpublic static final String URI_DETAILS_PREFIX = \"details\";\n\n\t/**\n\t * Returns a list uri given a base and relativePath\n\t *\n\t * @param base         such as content://my.provider.authority\n\t *                     *\n\t * @param relativePath /foo/bar\n\t *                     *\n\t * @return the list uri: content://my.provider.authority/list/foo/bar\n\t */\n\tpublic static Uri getListUri(@NonNull Uri base, @NonNull String relativePath) {\n\t\treturn Uri.withAppendedPath(Uri.withAppendedPath(base, \"list\"),\n\t\t\t\trelativePath);\n\t}\n\n\t/**\n\t * Returns a details uri given a base and relativePath\n\t *\n\t * @param base         such as content://my.provider.authority\n\t *                     *\n\t * @param relativePath /foo/bar\n\t *                     *\n\t * @return the details uri: content://my.provider.authority/details/foo/bar\n\t */\n\t@NonNull\n\tpublic static Uri getDetailsUri(@NonNull Uri base, @NonNull String relativePath) {\n\t\treturn Uri.withAppendedPath(Uri.withAppendedPath(base, \"details\"),\n\t\t\t\trelativePath);\n\t}\n\n\t/**\n\t * Returns only the scheme and authority parts.\n\t *\n\t * @param uri like content://my.provider.authority/details/foo/bar\n\t *            *\n\t * @return uri with only scheme and authority: content://my.provider.authority\n\t */\n\tpublic static Uri getBase(@NonNull Uri uri) {\n\t\treturn Uri.parse(uri.getScheme() + \"://\" + uri.getAuthority());\n\t}\n\n\t/**\n\t * Note that /ACTION will return \"/\".\n\t *\n\t * @param uri like content://my.provider.authority/ACTION/foo/bar\n\t *            *\n\t * @return relative path without action part like /foo/bar\n\t */\n\t@NonNull\n\tpublic static String getRelativePath(@NonNull Uri uri) {\n\t\treturn getRelativePath(uri.getPath());\n\t}\n\n\t@NonNull\n\tpublic static String getRelativePath(@NonNull String path) {\n\t\tvar i = path.indexOf(\"/\");\n\t\tif (i == 0) {\n\t\t\treturn getRelativePath(path.substring(1));\n\t\t} else if (i < 0) {\n\t\t\treturn \"/\";\n\t\t} else {\n\t\t\treturn path.substring(i);\n\t\t}\n\t}\n\n\t/**\n\t * @param path like /foo/bar\n\t *             *\n\t * @return first part of path like foo\n\t */\n\t@NonNull\n\tpublic static String firstPart(@NonNull String path) {\n\t\tvar i = path.indexOf(\"/\");\n\t\tif (i == 0) {\n\t\t\treturn firstPart(path.substring(1));\n\t\t} else if (i > 0) {\n\t\t\treturn path.substring(0, i);\n\t\t} else {\n\t\t\t// No slashes\n\t\t\treturn path;\n\t\t}\n\t}\n\n\n\t/**\n\t * If nothing remains, returns the empty string.\n\t *\n\t * @param path like /foo/bar/baz\n\t *             *\n\t * @return the bit after first like bar/baz (without starting slash)\n\t */\n\t@NonNull\n\tpublic static String restPart(@NonNull String path) {\n\t\tvar i = path.indexOf(\"/\");\n\t\tif (i == 0) {\n\t\t\treturn restPart(path.substring(1));\n\t\t} else if (i > 0) {\n\t\t\treturn path.substring(i + 1);\n\t\t} else {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tpublic static int matchUri(@NonNull Uri uri) {\n\t\treturn matchPath(uri.getPath());\n\t}\n\n\n\t/**\n\t * Since UriMatcher isn't as good as it should be, this implements the matching I want.\n\t *\n\t * @param path like /foo/bar/baz\n\t *             *\n\t * @return type of the path\n\t */\n\tpublic static int matchPath(@Nullable String path) {\n\t\twhile (true) {\n\t\t\tif (path != null && ((CharSequence) path).length() != 0) {\n\t\t\t\tString var10000;\n\t\t\t\tif (path.startsWith(\"/\")) {\n\t\t\t\t\tvar10000 = path.substring(1);\n\n\t\t\t\t\tpath = var10000;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tvar10000 = firstPart(path).toLowerCase(Locale.ROOT);\n\n\t\t\t\tString fp = var10000;\n\t\t\t\tif (Objects.equals(fp, \"list\")) {\n\t\t\t\t\treturn 102;\n\t\t\t\t}\n\n\t\t\t\tif (Objects.equals(fp, \"details\")) {\n\t\t\t\t\tif (((CharSequence) restPart(path)).length() == 0) {\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn 103;\n\t\t\t\t}\n\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\treturn 101;\n\t\t}\n\t}\n\n\t/**\n\t * Join two pieces together, separated by a /\n\t *\n\t * @param path1 like /foo\n\t *              *\n\t * @param path2 like bar\n\t *              *\n\t * @return /foo/bar\n\t */\n\t@NonNull\n\tpublic static String join(@NonNull String path1, @NonNull String path2) {\n\t\tif (path1.endsWith(\"/\")) {\n\t\t\tif (path2.startsWith(\"/\")) {\n\t\t\t\treturn path1 + path2.substring(1);\n\t\t\t} else {\n\t\t\t\treturn path1 + path2;\n\t\t\t}\n\t\t} else {\n\t\t\tif (path2.startsWith(\"/\")) {\n\t\t\t\treturn path1 + path2;\n\t\t\t} else {\n\t\t\t\treturn path1 + \"/\" + path2;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/ProviderManager.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.android.provider;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ProviderInfo;\nimport android.net.Uri;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * This class handles things related to (possibly 3rd party) providers.\n */\npublic final class ProviderManager {\n\n\tpublic final String METADATA_PROTOCOL_VERSION = \"protocolVersion\";\n\tpublic final String METADATA_REQUIRES_CONFIG = \"requiresConfig\";\n\tpublic final String METADATA_SETTINGS_ACTIVITY = \"settingsActivity\";\n\n\tprivate final Context applicationContext;\n\n\tpublic ProviderManager(@NonNull Context context) {\n\t\tthis.applicationContext = context.getApplicationContext();\n\t}\n\n\t/**\n\t * @return a list of providers which are available for use/setup.\n\t */\n\t@NonNull\n\tpublic List<Provider> getAvailableProviders() {\n\t\tArrayList<Provider> availableUris = new ArrayList<>();\n\t\tPackageManager pm = this.applicationContext.getPackageManager();\n\t\tvar var10000 = pm\n\t\t\t\t.queryIntentContentProviders(\n\t\t\t\t\t\tnew Intent(com.nononsenseapps.notepad.providercontract.ProviderContract.ACTION_PROVIDER),\n\t\t\t\t\t\tPackageManager.GET_META_DATA);\n\n\t\tfor (var resolveInfo : var10000) {\n\t\t\tvar metadata = resolveInfo.providerInfo.metaData;\n\t\t\tif (providerHasValidMetadata(metadata)) {\n\t\t\t\tavailableUris.add(new Provider(pm, resolveInfo.providerInfo));\n\t\t\t}\n\t\t}\n\n\t\treturn availableUris;\n\t}\n\n\t/**\n\t * @return a list of providers which are available for use. Note that a provider might\n\t * appear more than once here, if it's been configured with different settings\n\t * (different folders/user accounts, etc).\n\t */\n\t// First get all providers which do not require configuration\n\t// Instead of wrapping code in multiple ifs\n\t// TODO include providers which have been setup by user\n\t@NonNull\n\tpublic ArrayList<Provider> getConfiguredProviders() {\n\t\tvar availableUris = new ArrayList<Provider>();\n\t\tvar pm = applicationContext.getPackageManager();\n\t\tvar resolveInfos = pm.queryIntentContentProviders(\n\t\t\t\tnew Intent(com.nononsenseapps.notepad.providercontract.ProviderContract.ACTION_PROVIDER),\n\t\t\t\tPackageManager.GET_META_DATA);\n\t\tfor (var resolveInfo : resolveInfos) {\n\t\t\tvar metadata = resolveInfo.providerInfo.metaData;\n\t\t\tif (providerHasValidMetadata(metadata) && !providerRequiresConfig(metadata)) {\n\t\t\t\tavailableUris.add(new Provider(pm, resolveInfo.providerInfo));\n\t\t\t}\n\t\t}\n\n\t\treturn availableUris;\n\t}\n\n\t/**\n\t * Checks that a provider specifies correct metadata.\n\t *\n\t * @param metadata for provider\n\t *                 *\n\t * @return true or false\n\t */\n\tpublic boolean providerHasValidMetadata(@NonNull Bundle metadata) {\n\t\t// Only one protocol level atm\n\t\tvar result = 1 == metadata.getInt(METADATA_PROTOCOL_VERSION, -1);\n\n\t\t// If config is required, then a settingsactivity must be specified\n\t\tif (result && metadata.getBoolean(METADATA_REQUIRES_CONFIG, false)) {\n\t\t\tresult = metadata.containsKey(METADATA_SETTINGS_ACTIVITY);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * @param metadata for a given provider\n\t *                 *\n\t * @return true if provider is valid and specifies no required config\n\t */\n\tpublic boolean providerRequiresConfig(@NonNull Bundle metadata) {\n\t\treturn metadata.getBoolean(METADATA_REQUIRES_CONFIG, false);\n\t}\n\n\n\tpublic static final class Provider {\n\t\t@NonNull\n\t\tprivate final String authority;\n\t\t@NonNull\n\t\tprivate final Uri uriBase;\n\t\t@NonNull\n\t\tprivate final Uri uriList;\n\t\t@NonNull\n\t\tprivate final Uri uriDetails;\n\t\t@NonNull\n\t\tprivate final String label;\n\t\tprivate final int icon;\n\n\t\tpublic Provider(@NonNull PackageManager pm, @NonNull ProviderInfo providerInfo) {\n\n\t\t\tthis.label = providerInfo.loadLabel(pm).toString();\n\t\t\tthis.authority = providerInfo.authority;\n\t\t\tthis.uriBase = Uri.parse(\"content://\" + this.authority);\n\t\t\tthis.uriList = Uri.withAppendedPath(this.uriBase, \"/list\");\n\t\t\tthis.uriDetails = Uri.withAppendedPath(this.uriBase, \"/details\");\n\t\t\tthis.icon = providerInfo.getIconResource();\n\t\t\tif (null != providerInfo.metaData) {\n\t\t\t\t// Optional stuff like settingsActivity and capabilities\n\t\t\t\t// String settingsActivity = providerInfo.metaData.getString(\"settingsActivity\");\n\t\t\t}\n\t\t}\n\n\t\t@NonNull\n\t\tpublic String getAuthority() {\n\t\t\treturn this.authority;\n\t\t}\n\n\t\t@NonNull\n\t\tpublic Uri getUriBase() {\n\t\t\treturn this.uriBase;\n\t\t}\n\n\t\t@NonNull\n\t\tpublic Uri getUriList() {\n\t\t\treturn this.uriList;\n\t\t}\n\n\t\t@NonNull\n\t\tpublic Uri getUriDetails() {\n\t\t\treturn this.uriDetails;\n\t\t}\n\n\t\t@NonNull\n\t\tpublic String getLabel() {\n\t\t\treturn this.label;\n\t\t}\n\n\t\tpublic int getIcon() {\n\t\t\treturn this.icon;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/TextFileProvider.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.android.provider;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.net.Uri;\nimport android.os.Environment;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport com.nononsenseapps.notepad.BuildConfig;\nimport com.nononsenseapps.notepad.providercontract.ProviderContract;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class TextFileProvider extends ContentProvider {\n\n\t// TODO is this whole com.nononsenseapps.notepad.android.provider namespace useless ?\n\n\t/**\n\t * Corresponds to <i>android:authorities=\"${applicationId}.TESTPROVIDER.AUTHORITY\"</i>\n\t * in AndroidManifest.xml\n\t */\n\tpublic static final String AUTHORITY = BuildConfig.APPLICATION_ID + \".TESTPROVIDER.AUTHORITY\";\n\n\tprivate static final String TAG = \"TextFileProvider\";\n\n\t// This urimatcher converts incoming URIs to corresponding uricodes\n\tprivate static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);\n\tprivate static final int URI_ROOT = 101;\n\tprivate static final int URI_LIST = 102;\n\tprivate static final int URI_DETAILS = 103;\n\n\tprivate static final String TYPE_NONONSENSENOTES_ITEM = \"vnd.android.cursor.item/vnd.nononsensenotes.item\";\n\n\t// Add uris to match (initial slash supported from JELLY_BEAN_MR2)\n\tstatic {\n\t\t// No item is specified, corresponds to listing all top-level items\n\t\tsUriMatcher.addURI(AUTHORITY, \"/list\", URI_ROOT);\n\t\t// List all items which are children of the URI (but not the URI-item itself)\n\t\tsUriMatcher.addURI(AUTHORITY, \"/list/*\", URI_LIST);\n\t\t// Return the single item at the specified URI\n\t\tsUriMatcher.addURI(AUTHORITY, \"/details/*\", URI_DETAILS);\n\t}\n\n\n\tprivate String mRootPath;\n\tprivate FileFilter mFileFilter;\n\n\tpublic TextFileProvider() {\n\t}\n\n\t@Override\n\tpublic int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {\n\t\t// Implement this to handle requests to delete one or more rows.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic String getType(@NonNull Uri uri) {\n\t\t// TODO: Implement this to handle requests for the MIME type of the data\n\t\t// at the given URI.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic Uri insert(@NonNull Uri uri, ContentValues values) {\n\t\t// TODO: Implement this to handle requests to insert a new row.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic boolean onCreate() {\n\t\tmRootPath = Environment.getExternalStorageDirectory().getPath();\n\t\tmFileFilter = new FileFilter() {\n\n\t\t\t/**\n\t\t\t * Indicating whether a specific file should be included in a pathname list.\n\t\t\t *\n\t\t\t * @param pathname the abstract file to check.\n\t\t\t * @return {@code true} if the file should be included, {@code false}\n\t\t\t * otherwise.\n\t\t\t */\n\t\t\t@Override\n\t\t\tpublic boolean accept(File pathname) {\n\t\t\t\tif (pathname.isDirectory()) {\n\t\t\t\t\treturn true;\n\t\t\t\t} else {\n\t\t\t\t\treturn true;\n\t\t\t\t\t//return pathname.getName().toLowerCase().endsWith(\".txt\");\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic Cursor query(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\tString[] selectionArgs, String sortOrder) {\n\n\t\tLog.d(TAG, \"Uri: \" + uri.getAuthority() + \", \" + uri.getPath() + \", \" + uri.getQuery());\n\n\t\tString relativePath = switch (sUriMatcher.match(uri)) {\n\t\t\tcase URI_ROOT -> \"/\";\n\t\t\tcase URI_LIST -> ProviderHelper.getRelativePath(uri);\n\t\t\tdefault -> throw new IllegalArgumentException(\"Unknown path: \" + uri);\n\t\t};\n\n\t\tfinal File filePath = new File(ProviderHelper.join(mRootPath, relativePath));\n\t\tFile[] files = filePath.listFiles(mFileFilter);\n\t\tLog.d(TAG, \"Listing: \" + filePath.getPath() + \", files: \" + (files == null ? 0 : files.length));\n\n\t\tif (files == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Sort by name and path\n\t\tList<File> fileList = Arrays.asList(files);\n\t\tCollections.sort(fileList);\n\n\t\t// Projection is ProviderContract.sMainListProjection\n\t\tMatrixCursor mc = new MatrixCursor(projection, fileList.size());\n\n\t\tfor (File file : fileList) {\n\t\t\tmc.addRow(new Object[] { ProviderHelper.join(relativePath, file.getName()),\n\t\t\t\t\tProviderContract.getTypeMask(file.isDirectory() ? ProviderContract.TYPE_FOLDER : ProviderContract.TYPE_DATA,\n\t\t\t\t\t\t\tProviderContract.TYPE_DESCRIPTION),\n\t\t\t\t\tfile.getName(), null, null, null });\n\t\t}\n\n\t\treturn mc;\n\t}\n\n\t@Override\n\tpublic int update(@NonNull Uri uri, ContentValues values, String selection,\n\t\t\t\t\t  String[] selectionArgs) {\n\t\t// TODO: Implement this to handle requests to update one or more rows.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/dashclock/DashclockPrefActivity.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.dashclock;\n\nimport android.os.Bundle;\nimport android.view.MenuItem;\nimport android.view.Window;\n\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.nononsenseapps.notepad.R;\n\n/**\n * holds the preferences for dashclock integration. See {@link DashclockPrefsFragment}\n */\npublic class DashclockPrefActivity extends AppCompatActivity {\n\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsupportRequestWindowFeature(Window.FEATURE_ACTION_BAR);\n\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\tsetContentView(R.layout.activity_dashclock_settings);\n\n\t\tif (getSupportActionBar() != null) {\n\t\t\tgetSupportActionBar().setIcon(R.drawable.ic_stat_notification_edit);\n\t\t\tgetSupportActionBar().setDisplayHomeAsUpEnabled(true);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tif (item.getItemId() == android.R.id.home) {\n\t\t\tfinish();\n\t\t\treturn true;\n\t\t}\n\n\t\treturn super.onOptionsItemSelected(item);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/dashclock/DashclockPrefsFragment.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.dashclock;\n\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.os.Bundle;\n\nimport androidx.annotation.Nullable;\nimport androidx.preference.ListPreference;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport java.util.ArrayList;\n\n/**\n * This app can be used with dashclock. This fragment shows the settings to configure\n * the dashclock plug-in\n */\npublic class DashclockPrefsFragment extends PreferenceFragmentCompat {\n\n\t/**\n\t * A preference value change listener that updates the preference's summary\n\t * to reflect its new value.\n\t */\n\tprivate static final Preference.OnPreferenceChangeListener\n\t\t\tsBindPreferenceSummaryToValueListener = (preference, value) -> {\n\t\tString stringValue = value.toString();\n\n\t\tif (preference instanceof ListPreference listPreference) {\n\t\t\t// For list preferences, look up the correct display value in\n\t\t\t// the preference's 'entries' list.\n\t\t\tint index =\n\t\t\t\t\tlistPreference.findIndexOfValue(stringValue);\n\n\t\t\t// Set the summary to reflect the new value.\n\t\t\tpreference.setSummary(index >= 0 ?\n\t\t\t\t\tlistPreference\n\t\t\t\t\t\t\t.getEntries()[index] :\n\t\t\t\t\tnull);\n\n\t\t} else {\n\t\t\t// For all other preferences, set the summary to the value's\n\t\t\t// simple string representation.\n\t\t\tpreference.setSummary(stringValue);\n\t\t}\n\t\treturn true;\n\t};\n\n\t@Override\n\tpublic void onCreatePreferences(@Nullable Bundle savInstState, String rootKey) {\n\n\t\taddPreferencesFromResource(R.xml.dashclock_pref_general);\n\n\t\t// Bind the summaries of EditText/List/Dialog/Ringtone preferences\n\t\t// to their values. When their values change, their summaries are\n\t\t// updated to reflect the new value, per the Android Design\n\t\t// guidelines.\n\t\tbindPreferenceSummaryToValue(findPreference(\"list_spinner\"));\n\t\tsetEntries(getActivity(), findPreference(\"list_spinner\"));\n\n\t\tbindPreferenceSummaryToValue(findPreference(\"list_due_upper_limit\"));\n\t}\n\n\t/**\n\t * Binds a preference's summary to its value. More specifically, when the\n\t * preference's value is changed, its summary (line of text below the\n\t * preference title) is updated to reflect the value. The summary is also\n\t * immediately updated upon calling this method. The exact display format\n\t * is\n\t * dependent on the type of preference.\n\t *\n\t * @see #sBindPreferenceSummaryToValueListener\n\t */\n\tprivate static void bindPreferenceSummaryToValue(Preference preference) {\n\t\t// Set the listener to watch for value changes.\n\t\tpreference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);\n\n\t\t// Trigger the listener immediately with the preference's\n\t\t// current value.\n\t\tsBindPreferenceSummaryToValueListener.onPreferenceChange(preference,\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(preference.getContext())\n\t\t\t\t\t\t.getString(preference.getKey(), \"\")\n\t\t);\n\t}\n\n\t/**\n\t * Reads the lists from database. Also adds \"All lists\" as the first item.\n\t */\n\tprivate static void setEntries(Context context, ListPreference listSpinner) {\n\n\t\tArrayList<CharSequence> entries = new ArrayList<>();\n\t\tArrayList<CharSequence> values = new ArrayList<>();\n\n\t\t// Start with all lists\n\t\tentries.add(\"All lists\");\n\t\tvalues.add(\"-1\");\n\t\t// Set it as the default value also\n\t\t//listSpinner.setDefaultValue(\"-1\");\n\n\t\tCursor cursor = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(TaskList.URI, TaskList.Columns.FIELDS, null, null,\n\t\t\t\t\t\tTaskList.Columns.TITLE);\n\t\tif (cursor != null) {\n\t\t\tif (!cursor.isClosed() && !cursor.isAfterLast()) {\n\t\t\t\twhile (cursor.moveToNext()) {\n\t\t\t\t\tentries.add(cursor.getString(1));\n\t\t\t\t\tvalues.add(Long.toString(cursor.getLong(0)));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcursor.close();\n\t\t}\n\n\t\t// Set the values\n\t\tif (listSpinner != null) {\n\t\t\tlistSpinner.setEntries(entries.toArray(new CharSequence[0]));\n\t\t\tlistSpinner.setEntryValues(values.toArray(new CharSequence[0]));\n\t\t\tlistSpinner.setSummary(listSpinner.getEntry());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/dashclock/TasksExtension.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.dashclock;\n\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.database.Cursor;\n\nimport androidx.preference.PreferenceManager;\n\nimport com.google.android.apps.dashclock.api.DashClockExtension;\nimport com.google.android.apps.dashclock.api.ExtensionData;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Calendar;\nimport java.util.GregorianCalendar;\nimport java.util.stream.Stream;\n\npublic class TasksExtension extends DashClockExtension {\n\n\tpublic static final String DUEDATE_SORT_TYPE = \"CASE WHEN \" +\n\t\t\tTask.Columns.DUE + \" IS NULL OR \" +\n\t\t\tTask.Columns.DUE + \" IS '' THEN 1 ELSE 0 END, \" +\n\t\t\tTask.Columns.DUE;\n\tprivate static final String WHERE_LIST_IS_AND = Task.Columns.DBLIST\n\t\t\t+ \" IS ? AND \";\n\tprivate static final String WHERE_DATE_IS = Task.Columns.COMPLETED +\n\t\t\t\" IS NULL AND \" +\n\t\t\tTask.Columns.DUE + \" IS NOT NULL AND \" +\n\t\t\tTask.Columns.DUE + \" <= ? \";\n\tprivate static final String WHERE_ALL_NOTDONE = Task.Columns.COMPLETED\n\t\t\t+ \" IS NULL\";\n\n\tprivate String[] toA(final String... args) {\n\t\treturn args;\n\t}\n\n\tprivate String[] appendTo(final String[] array, final String... items) {\n\t\treturn Stream.concat(Arrays.stream(array), Arrays.stream(items)).toArray(String[]::new);\n\t}\n\n\tfinal static String[] NOTEFIELDS = new String[] { \"_id\", \"title\", \"note\", \"duedate\" };\n\n\t@Override\n\tprotected void onInitialize(boolean isReconnect) {\n\t\tsuper.onInitialize(isReconnect);\n\t\t// Watch the notes URI\n\t\taddWatchContentUris(toA(TaskList.URI.toString(), Task.URI.toString()));\n\t}\n\n\t@Override\n\tprotected void onUpdateData(int reason) {\n\n\t\t// Get preferences\n\t\tfinal SharedPreferences prefs = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(this);\n\n\t\tfinal long listId = Long.parseLong(prefs\n\t\t\t\t.getString(\"list_spinner\", \"-1\"));\n\n\t\tfinal boolean showOverdue = prefs.getBoolean(\"show_overdue\", true);\n\t\tfinal String upperLimit = prefs.getString(\"list_due_upper_limit\", getString(R.string.dashclock_pref_today));\n\t\tfinal boolean showSingle = prefs.getBoolean(\"show_single_only\", false);\n\t\tfinal boolean showHeader = prefs.getBoolean(\"show_header\", true);\n\n\t\tfinal ArrayList<Task> notes = getNotesFromDB(listId, upperLimit);\n\n\t\t// Show overdue?\n\t\tif (!showOverdue) {\n\t\t\tremoveOverdue(notes);\n\t\t}\n\n\t\tif (showSingle && notes.size() > 1) {\n\t\t\tfinal Task first = notes.get(0);\n\t\t\tnotes.clear();\n\t\t\tnotes.add(first);\n\t\t}\n\n\t\tif (notes.isEmpty()) {\n\t\t\tpublishUpdate(null);\n\t\t} else {\n\n\t\t\tfinal String short_header = getString(\n\t\t\t\t\tR.string.dashclock_tasks_count, notes.size());\n\n\t\t\tfinal String long_header;\n\n\t\t\t// If no header is to be displayed, show title of first\n\t\t\tif (showHeader) {\n\t\t\t\tlong_header = getHeader(listId);\n\t\t\t} else {\n\t\t\t\tlong_header = notes.get(0).title;\n\t\t\t}\n\n\t\t\tfinal Intent noteIntent = new Intent();\n\t\t\tif (notes.size() > 1) {\n\t\t\t\tnoteIntent\n\t\t\t\t\t\t.setAction(Intent.ACTION_VIEW)\n\t\t\t\t\t\t.setData(TaskList.getUri(notes.get(0).dblist))\n\t\t\t\t\t\t.putExtra(Task.TABLE_NAME, notes.get(0)._id);\n\t\t\t} else {\n\t\t\t\tnoteIntent\n\t\t\t\t\t\t.setAction(Intent.ACTION_EDIT)\n\t\t\t\t\t\t.setData(Task.getUri(notes.get(0)._id))\n\t\t\t\t\t\t.putExtra(Task.Columns.DBLIST, notes.get(0).dblist.longValue());\n\t\t\t}\n\n\t\t\t// Publish the extension data update.\n\t\t\tpublishUpdate(new ExtensionData().visible(true)\n\t\t\t\t\t.icon(R.drawable.ic_stat_notification_edit)\n\t\t\t\t\t.status(short_header).expandedTitle(long_header)\n\t\t\t\t\t.expandedBody(getBody(notes, showHeader))\n\t\t\t\t\t.clickIntent(noteIntent));\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate void removeOverdue(final ArrayList<Task> notes) {\n\t\tfor (Task note : (ArrayList<Task>) notes.clone()) {\n\t\t\tif (note.due != null\n\t\t\t\t\t&& note.due < Calendar.getInstance().getTimeInMillis())\n\t\t\t\tnotes.remove(note);\n\t\t}\n\t}\n\n\tprivate String getBody(final ArrayList<Task> notes, final boolean showHeader) {\n\t\tString result = \"\";\n\t\tif (notes.size() == 1) {\n\t\t\tif (showHeader) {\n\t\t\t\t// Skip title if no header as the title is the header\n\t\t\t\tresult += notes.get(0).title;\n\t\t\t\tresult += \"\\n\";\n\t\t\t}\n\t\t\tresult += notes.get(0).note;\n\t\t} else {\n\t\t\tboolean first = true;\n\t\t\tboolean skippable = true;\n\t\t\tfor (Task note : notes) {\n\t\t\t\tif (!showHeader && skippable) {\n\t\t\t\t\t// Skip first\n\t\t\t\t\tskippable = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!first) result += \"\\n\";\n\t\t\t\tresult += note.title;\n\t\t\t\tfirst = false;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Return a list of notes respecting the constraints set in preferences.\n\t */\n\tprivate ArrayList<Task> getNotesFromDB(final long list, final String upperLimit) {\n\t\t// WHERE_LIST_IS, toA(list)\n\t\tString where = \"\";\n\t\tString[] whereArgs = new String[0];\n\t\tif (list > -1) {\n\t\t\twhere += WHERE_LIST_IS_AND;\n\t\t\twhereArgs = appendTo(whereArgs, Long.toString(list));\n\t\t}\n\n\t\twhere += getUpperQueryLimitWhere(upperLimit);\n\t\twhereArgs = getUpperQueryLimitWhereArgs(whereArgs, upperLimit);\n\n\t\tfinal Cursor cursor = getContentResolver().query(Task.URI,\n\t\t\t\tTask.Columns.FIELDS, where, whereArgs, DUEDATE_SORT_TYPE);\n\n\t\tfinal ArrayList<Task> result = new ArrayList<>();\n\t\tif (cursor != null) {\n\t\t\twhile (cursor.moveToNext()) {\n\t\t\t\tresult.add(new Task(cursor));\n\t\t\t}\n\t\t\tcursor.close();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Returns the list name, or \"Tasks\" if all lists are to be shown.\n\t */\n\tprivate String getHeader(final long list) {\n\t\tString header = getString(R.string.dashclock_tasks);\n\n\t\tif (list > -1) {\n\t\t\tfinal Cursor cursor = getContentResolver().query(TaskList.URI,\n\t\t\t\t\tTaskList.Columns.FIELDS, TaskList.Columns._ID + \" IS ?\",\n\t\t\t\t\tnew String[] { Long.toString(list) }, null);\n\t\t\tif (cursor != null) {\n\t\t\t\tif (!cursor.isClosed() && !cursor.isAfterLast()) {\n\t\t\t\t\tif (cursor.moveToNext()) {\n\t\t\t\t\t\theader = cursor.getString(1);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\n\t\treturn header;\n\t}\n\n\tprivate String getUpperQueryLimitWhere(final String upperLimit) {\n\t\tString where = WHERE_DATE_IS;\n\t\tif (getString(R.string.dashclock_pref_none).equals(upperLimit)) {\n\t\t\twhere = WHERE_ALL_NOTDONE;\n\t\t}\n\t\treturn where;\n\t}\n\n\tprivate String[] getUpperQueryLimitWhereArgs(final String[] whereArgs, final String upperLimit) {\n\t\tfinal GregorianCalendar gc = new GregorianCalendar();\n\t\tgc.set(GregorianCalendar.HOUR_OF_DAY, 23);\n\t\tgc.set(GregorianCalendar.MINUTE, 59);\n\t\tfinal long base = gc.getTimeInMillis();\n\t\tfinal long day = 24 * 60 * 60 * 1000;\n\t\tif (getString(R.string.dashclock_pref_today).equals(upperLimit)) {\n\t\t\treturn appendTo(whereArgs, Long.toString(gc.getTimeInMillis()));\n\t\t} else if (getString(R.string.dashclock_pref_tomorrow).equals(upperLimit)) {\n\t\t\tgc.setTimeInMillis(base + 1 * day);\n\t\t\treturn appendTo(whereArgs, Long.toString(gc.getTimeInMillis()));\n\t\t} else if (getString(R.string.dashclock_pref_next7).equals(upperLimit)) {\n\t\t\tgc.setTimeInMillis(base + 7 * day);\n\t\t\treturn appendTo(whereArgs, Long.toString(gc.getTimeInMillis()));\n\t\t} else {\n\t\t\treturn whereArgs;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/DAO.java",
    "content": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\n\nimport com.mobeta.android.dslv.DragSortListView;\nimport com.nononsenseapps.helpers.NnnLogger;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\npublic abstract class DAO {\n\n\tprivate static final String whereIdIs = \"\" + BaseColumns._ID + \" IS ?\";\n\n\t/**\n\t * Append where is id ? to string\n\t */\n\tpublic static String whereIdIs(final String orgWhere) {\n\t\tfinal StringBuilder sb = new StringBuilder();\n\t\tif (orgWhere != null) {\n\t\t\tsb.append(\"(\");\n\t\t\tsb.append(orgWhere);\n\t\t\tsb.append(\") AND \");\n\t\t}\n\t\tsb.append(BaseColumns._ID).append(\" IS ?\");\n\t\treturn sb.toString();\n\t}\n\n\tpublic String[] whereIdArg() {\n\t\treturn new String[] { Long.toString(_id) };\n\t}\n\n\tpublic static String[] whereIdArg(final long _id) {\n\t\treturn new String[] { Long.toString(_id) };\n\t}\n\n\t/**\n\t * Append the id argument to array\n\t */\n\tpublic static String[] whereIdArg(final long _id,\n\t\t\t\t\t\t\t\t\t  final String[] orgWhereArgs) {\n\t\tif (orgWhereArgs == null) {\n\t\t\treturn whereIdArg(_id);\n\t\t} else {\n\t\t\treturn joinArrays(orgWhereArgs, whereIdArg(_id));\n\t\t}\n\t}\n\n\tpublic static String[] prefixArray(final String prefix, final String[] array) {\n\t\tfinal String[] result = new String[array.length];\n\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\tresult[i] = \"\" + prefix + array[i];\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static String[] joinArrays(final String[]... arrays) {\n\t\tfinal ArrayList<String> list = new ArrayList<>();\n\t\tfor (final String[] array : arrays) {\n\t\t\tif (array != null) {\n\t\t\t\tlist.addAll(Arrays.asList(array));\n\t\t\t}\n\t\t}\n\t\treturn list.toArray(new String[0]);\n\t}\n\n\t/**\n\t * Examples: <br/>\n\t * [] -> \"\" <br/>\n\t * [a] -> \"a\" <br/>\n\t * [a, b] -> \"a,b\" <br/>\n\t */\n\tpublic static String arrayToCommaString(final long... array) {\n\t\tStringBuilder result = new StringBuilder();\n\t\tfor (final long val : array) {\n\t\t\tfinal String txt = Long.toString(val);\n\t\t\tif (result.length() > 0) result.append(\",\");\n\t\t\tresult.append(txt);\n\t\t}\n\t\treturn result.toString();\n\t}\n\n\tpublic static String arrayToCommaString(final String... array) {\n\t\treturn arrayToCommaString(\"\", array);\n\t}\n\n\t/**\n\t * Example (prefix=t.): [] -> \"\" [a] -> \"t.a\" [a, b] -> \"t.a,t.b\"\n\t */\n\tpublic static String arrayToCommaString(final String prefix,\n\t\t\t\t\t\t\t\t\t\t\tfinal String[] array) {\n\t\treturn arrayToCommaString(prefix, array, \"\");\n\t}\n\n\t/**\n\t * Example (prefix=t., suffix=.45): [] -> \"\" [a] -> \"t.a.45\" [a, b] ->\n\t * \"t.a.45,t.b.45\"\n\t *\n\t * In addition, the txt itself can be referenced using %1$s in either prefix\n\t * or suffix. The prefix can be referenced as %2$s in suffix, and\n\t * vice-versa.\n\t *\n\t * So the following is valid:\n\t *\n\t * (prefix='t.', suffix=' AS %2$s%1$s')\n\t *\n\t * [listId] -> t.listId AS t.listId\n\t */\n\tprotected static String arrayToCommaString(final String pfx,\n\t\t\t\t\t\t\t\t\t\t\t   final String[] array, final String sfx) {\n\t\tStringBuilder result = new StringBuilder();\n\t\tfor (final String txt : array) {\n\t\t\tif (result.length() > 0) result.append(\",\");\n\t\t\tresult.append(String.format(pfx, txt, sfx));\n\t\t\tresult.append(txt);\n\t\t\tresult.append(String.format(sfx, txt, pfx));\n\t\t}\n\t\treturn result.toString();\n\t}\n\n\t/**\n\t * Second and Third value is wrapped in '' ticks, NOT the first.\n\t *\n\t * For example, <pre>asEmptyCommaStringExcept(new String[] { \"a\", \"b\", \"c\", \"d\" , \"e\" },\"b\",\n\t * \"HELLO\",\"c\",\"WORLD\",\"d\",\"!!!!\")</pre> will return <pre>null,HELLO,'WORLD','!!!!',null</pre>\n\t *\n\t * So it is useful to return a name & value pair for the header of the {@link DragSortListView}\n\t * when it is sorted by date. In that case you use this function to run a query that returns\n\t * special values: see {@link Task#CREATE_SECTIONED_DATE_VIEW}\n\t */\n\tprotected static String asEmptyCommaStringExcept(final String[] asColumns,\n\t\t\t\t\t\t\t\t\t\t\t\t\t final String exceptCol1, final String asValue1,\n\t\t\t\t\t\t\t\t\t\t\t\t\t final String exceptCol2, final String asValue2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t final String exceptCol3, final String asValue3) {\n\t\tStringBuilder result = new StringBuilder();\n\t\tfor (final String colName : asColumns) {\n\t\t\tif (result.length() > 0) result.append(\",\");\n\n\t\t\tif (colName.equals(exceptCol2)) {\n\t\t\t\tresult.append(\"'\").append(asValue2).append(\"'\");\n\t\t\t} else if (colName.equals(exceptCol3)) {\n\t\t\t\tresult.append(\"'\").append(asValue3).append(\"'\");\n\t\t\t} else if (colName.equals(exceptCol1)) {\n\t\t\t\tresult.append(asValue1);\n\t\t\t} else {\n\t\t\t\tresult.append(\"null\");\n\t\t\t}\n\t\t}\n\t\treturn result.toString();\n\t}\n\n\t/**\n\t * Third and Fourth value is wrapped in '' ticks, NOT the first and second.\n\t */\n\tprotected static String asEmptyCommaStringExcept(final String[] asColumns,\n\t\t\t\t\t\t\t\t\t\t\t\t\t final String exceptCol1, final String asValue1,\n\t\t\t\t\t\t\t\t\t\t\t\t\t final String exceptCol2, final String asValue2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t final String exceptCol3, final String asValue3,\n\t\t\t\t\t\t\t\t\t\t\t\t\t final String exceptCol4, final String asValue4) {\n\t\tStringBuilder result = new StringBuilder();\n\t\tfor (final String colName : asColumns) {\n\t\t\tif (result.length() > 0) result.append(\",\");\n\n\t\t\tif (colName.equals(exceptCol3)) {\n\t\t\t\tresult.append(\"'\").append(asValue3).append(\"'\");\n\t\t\t} else if (colName.equals(exceptCol4)) {\n\t\t\t\tresult.append(\"'\").append(asValue4).append(\"'\");\n\t\t\t} else if (colName.equals(exceptCol2)) {\n\t\t\t\tresult.append(asValue2);\n\t\t\t} else if (colName.equals(exceptCol1)) {\n\t\t\t\tresult.append(asValue1);\n\t\t\t} else {\n\t\t\t\tresult.append(\"null\");\n\t\t\t}\n\t\t}\n\t\treturn result.toString();\n\t}\n\n\tpublic Uri getUri() {\n\t\treturn Uri.withAppendedPath(getBaseUri(), Long.toString(_id));\n\t}\n\n\tpublic Uri getBaseUri() {\n\t\treturn Uri.withAppendedPath(\n\t\t\t\tUri.parse(MyContentProvider.SCHEME\n\t\t\t\t\t\t+ MyContentProvider.AUTHORITY), getTableName());\n\t}\n\n\tpublic long _id = -1;\n\n\tpublic synchronized boolean update(final Context context, final SQLiteDatabase db) {\n\t\tint result = 0;\n\t\tdb.beginTransaction();\n\n\t\ttry {\n\t\t\tif (_id > 0) {\n\t\t\t\tresult += db.update(getTableName(), getContent(), whereIdIs, whereIdArg());\n\t\t\t}\n\n\t\t\tif (result > 0) {\n\t\t\t\tdb.setTransactionSuccessful();\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\t\t\tNnnLogger.exception(e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tdb.endTransaction();\n\t\t}\n\n\t\tif (result > 0) {\n\t\t\tnotifyProviderOnChange(context);\n\t\t}\n\n\t\treturn result > 0;\n\t}\n\n\tpublic synchronized Uri insert(final Context context, final SQLiteDatabase db) {\n\t\tUri retval;\n\t\tdb.beginTransaction();\n\t\ttry {\n\t\t\tbeforeInsert(context, db);\n\n\t\t\tfinal long id = db.insert(getTableName(), null, getContent());\n\n\t\t\tif (id == -1) {\n\t\t\t\tthrow new SQLException(\"Insert failed in \" + getTableName());\n\t\t\t} else {\n\t\t\t\t_id = id;\n\t\t\t\tafterInsert(context, db);\n\t\t\t\tdb.setTransactionSuccessful();\n\t\t\t\tretval = getUri();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tNnnLogger.exception(e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tdb.endTransaction();\n\t\t}\n\n\t\tif (retval != null) {\n\t\t\tnotifyProviderOnChange(context);\n\t\t}\n\t\treturn retval;\n\t}\n\n\tpublic synchronized int remove(final Context context,\n\t\t\t\t\t\t\t\t   final SQLiteDatabase db) {\n\t\tfinal int result = db.delete(getTableName(), BaseColumns._ID + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(_id) });\n\n\t\tif (result > 1) {\n\t\t\tnotifyProviderOnChange(context);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic static void notifyProviderOnChange(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t  final Uri uri) {\n\t\ttry {\n\t\t\tcontext.getContentResolver().notifyChange(uri, null, false);\n\t\t} catch (UnsupportedOperationException e) {\n\t\t\t// Catch this for test suite. Mock provider cant notify\n\t\t}\n\t}\n\n\tprotected void notifyProviderOnChange(final Context context) {\n\t\tnotifyProviderOnChange(context, getUri());\n\t}\n\n\tpublic void setId(final Uri uri) {\n\t\t_id = Long.parseLong(uri.getLastPathSegment());\n\t}\n\n\tprotected void beforeInsert(final Context context, final SQLiteDatabase db) {}\n\n\tprotected void afterInsert(final Context context, final SQLiteDatabase db) {}\n\n\tprotected DAO() {}\n\n\tpublic abstract ContentValues getContent();\n\n\tprotected abstract String getTableName();\n\n\tpublic abstract String getContentType();\n\n\t/**\n\t * Convenience method for normal operations. Updates \"updated\" field.\n\t * Returns number of db-rows affected. Fail if < 1\n\t */\n\tpublic abstract int save(final Context context);\n\n\t/**\n\t * Delete object from database\n\t *\n\t * @return the number of rows deleted, or 0\n\t */\n\tpublic int delete(final Context context) {\n\t\tif (_id > 0) {\n\t\t\treturn context.getContentResolver().delete(getUri(), null, null);\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/DatabaseHandler.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteException;\nimport android.database.sqlite.SQLiteOpenHelper;\nimport android.provider.BaseColumns;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.RFC3339Date;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.prefs.Constants;\nimport com.nononsenseapps.notepad.sync.googleapi.GoogleTask;\nimport com.nononsenseapps.notepad.sync.googleapi.GoogleTaskList;\n\nimport java.util.Calendar;\nimport java.util.HashMap;\n\npublic class DatabaseHandler extends SQLiteOpenHelper {\n\n\tprivate static DatabaseHandler singleton;\n\n\tpublic static DatabaseHandler getInstance(final Context context) {\n\t\tif (singleton == null) {\n\t\t\tsingleton = new DatabaseHandler(context);\n\t\t}\n\t\treturn singleton;\n\t}\n\n\tprivate static final int DATABASE_VERSION = 15;\n\tpublic static final String DATABASE_NAME = \"nononsense_notes.db\";\n\n\tprivate final Context context;\n\tprivate final String testPrefix;\n\n\t/**\n\t * Should use the singleton for normal cases\n\t */\n\tprivate DatabaseHandler(Context context) {\n\t\tthis(context, \"\");\n\t}\n\n\t/**\n\t * Use only for JUNIT tests\n\t */\n\tpublic DatabaseHandler(Context context, String testPrefix) {\n\t\tsuper(context, testPrefix + DATABASE_NAME, null, DATABASE_VERSION);\n\t\t// Good idea to have the context that doesn't die with the window\n\t\tthis.context = context.getApplicationContext();\n\t\tthis.testPrefix = testPrefix;\n\t}\n\n\t@Override\n\tpublic void onOpen(SQLiteDatabase db) {\n\t\tsuper.onOpen(db);\n\t\tif (!db.isReadOnly()) {\n\t\t\t// Enable foreign key constraints\n\t\t\t// This would require android16\n\t\t\t// db.setForeignKeyConstraintsEnabled(true);\n\t\t\t// This works everywhere\n\t\t\tdb.execSQL(\"PRAGMA foreign_keys=ON;\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onCreate(SQLiteDatabase db) {\n\t\tdb.execSQL(TaskList.CREATE_TABLE);\n\t\tdb.execSQL(Task.CREATE_TABLE);\n\t\tdb.execSQL(Task.CREATE_DELETE_TABLE);\n\t\tdb.execSQL(Task.CREATE_HISTORY_TABLE);\n\t\tdb.execSQL(Notification.CREATE_TABLE);\n\t\tdb.execSQL(RemoteTaskList.CREATE_TABLE);\n\t\tdb.execSQL(RemoteTask.CREATE_TABLE);\n\n\t\tdb.execSQL(Notification.CREATE_JOINED_VIEW);\n\n\t\tdb.execSQL(Task.TRIGGER_PRE_INSERT);\n\t\tdb.execSQL(Task.TRIGGER_PRE_DELETE);\n\t\tdb.execSQL(Task.TRIGGER_POST_DELETE);\n\t\tdb.execSQL(Task.TRIGGER_MOVE_LIST);\n\t\tdb.execSQL(Task.CREATE_HISTORY_INSERT_TRIGGER);\n\t\tdb.execSQL(Task.CREATE_HISTORY_UPDATE_TRIGGER);\n\n\t\tdb.execSQL(RemoteTask.TRIGGER_LISTDELETE_CASCADE);\n\t\t// Mark as deleted when real item deleted\n\t\tdb.execSQL(RemoteTask.TRIGGER_REALDELETE_MARK);\n\t\tdb.execSQL(RemoteTaskList.TRIGGER_REALDELETE_MARK);\n\t\t// Create move list trigger\n\t\tdb.execSQL(RemoteTask.TRIGGER_MOVE_LIST);\n\n\t\t// Search tables\n\t\tdb.execSQL(Task.CREATE_FTS3_TABLE);\n\t\tdb.execSQL(Task.CREATE_FTS3_INSERT_TRIGGER);\n\t\tdb.execSQL(Task.CREATE_FTS3_UPDATE_TRIGGER);\n\t\tdb.execSQL(Task.CREATE_FTS3_DELETE_TRIGGER);\n\n\t\t// Delete search tables\n\t\tdb.execSQL(Task.CREATE_FTS3_DELETE_TABLE);\n\t\tdb.execSQL(Task.CREATE_FTS3_DELETED_INSERT_TRIGGER);\n\t\tdb.execSQL(Task.CREATE_FTS3_DELETED_UPDATE_TRIGGER);\n\t\tdb.execSQL(Task.CREATE_FTS3_DELETED_DELETE_TRIGGER);\n\n\t\tinitializedDB(db);\n\t}\n\n\tpublic static Cursor getLegacyLists(final SQLiteDatabase legacyDB) {\n\t\treturn legacyDB.rawQuery(\"SELECT lists.\"\n\t\t\t\t+ BaseColumns._ID\n\t\t\t\t+ \",lists.title,gtasklists.googleid,gtasklists.googleaccount\"\n\t\t\t\t+ \" FROM \" + LegacyDBHelper.NotePad.Lists.TABLE_NAME\n\t\t\t\t+ \" LEFT OUTER JOIN \"\n\t\t\t\t+ LegacyDBHelper.NotePad.GTaskLists.TABLE_NAME + \" ON (\"\n\t\t\t\t+ LegacyDBHelper.NotePad.Lists.TABLE_NAME + \".\"\n\t\t\t\t+ LegacyDBHelper.NotePad.Lists._ID + \" = \"\n\t\t\t\t+ LegacyDBHelper.NotePad.GTaskLists.TABLE_NAME + \".\"\n\t\t\t\t+ LegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_DB_ID + \")\"\n\t\t\t\t+ \" WHERE lists.deleted IS NOT 1\", null);\n\t}\n\n\tpublic static Cursor getLegacyNotes(final SQLiteDatabase legacyDB) {\n\t\treturn legacyDB.rawQuery(\"SELECT notes.\"\n\t\t\t\t\t\t+ BaseColumns._ID\n\t\t\t\t\t\t+ \",notes.title,notes.note,notes.duedate,notes.gtaskstatus,notes.list,notes.modified,gtasks.googleid,gtasks.googleaccount\"\n\t\t\t\t\t\t+ \" FROM \"\n\t\t\t\t\t\t+ LegacyDBHelper.NotePad.Notes.TABLE_NAME\n\t\t\t\t\t\t+ \" LEFT OUTER JOIN \"\n\t\t\t\t\t\t+ LegacyDBHelper.NotePad.GTasks.TABLE_NAME\n\t\t\t\t\t\t+ \" ON (\"\n\t\t\t\t\t\t+ LegacyDBHelper.NotePad.Notes.TABLE_NAME\n\t\t\t\t\t\t+ \".\"\n\t\t\t\t\t\t+ LegacyDBHelper.NotePad.Notes._ID\n\t\t\t\t\t\t+ \" = \"\n\t\t\t\t\t\t+ LegacyDBHelper.NotePad.GTasks.TABLE_NAME\n\t\t\t\t\t\t+ \".\"\n\t\t\t\t\t\t+ LegacyDBHelper.NotePad.GTasks.COLUMN_NAME_DB_ID\n\t\t\t\t\t\t+ \")\"\n\t\t\t\t\t\t+ \" WHERE notes.deleted IS NOT 1 AND notes.hiddenflag IS NOT 1\",\n\t\t\t\tnull);\n\t}\n\n\tpublic static Cursor getLegacyNotifications(final SQLiteDatabase legacyDB) {\n\t\treturn legacyDB.query(LegacyDBHelper.NotePad.Notifications.TABLE_NAME,\n\t\t\t\tnew String[] { \"time\", \"permanent\", \"noteid\" }, null,\n\t\t\t\tnull, null, null, null);\n\t}\n\n\tprivate void initializedDB(final SQLiteDatabase db) throws SQLiteException {\n\t\t// Load legacy DB if it exists\n\t\t// Open database and copy information\n\t\t// Remember to do try except\n\n\t\tdb.beginTransaction();\n\t\ttry {\n\t\t\tfinal HashMap<Long, Long> listIDMap = new HashMap<>();\n\t\t\tfinal HashMap<Long, Long> taskIDMap = new HashMap<>();\n\t\t\tfinal LegacyDBHelper legacyDBHelper = new LegacyDBHelper(context, testPrefix);\n\t\t\tfinal SQLiteDatabase legacyDB = legacyDBHelper.getReadableDatabase();\n\n\t\t\t// First copy lists\n\t\t\t// if there's no legacy DB, this call crashes and the whole try-block is skipped\n\t\t\tCursor c = getLegacyLists(legacyDB);\n\n\t\t\twhile (!c.isClosed() && c.moveToNext()) {\n\t\t\t\tTaskList tl = new TaskList();\n\t\t\t\ttl.title = c.getString(1);\n\t\t\t\ttl.updated = Calendar.getInstance().getTimeInMillis();\n\n\t\t\t\t// insert into db\n\t\t\t\ttl.insert(context, db);\n\t\t\t\t// remember id\n\t\t\t\tlistIDMap.put(c.getLong(0), tl._id);\n\n\t\t\t\t// handle gtask info\n\t\t\t\tGoogleTaskList rl;\n\t\t\t\tif (c.getString(2) != null\n\t\t\t\t\t\t&& !c.getString(2).isEmpty()\n\t\t\t\t\t\t&& c.getString(3) != null\n\t\t\t\t\t\t&& !c.getString(3).isEmpty()) {\n\t\t\t\t\trl = new GoogleTaskList(tl._id, c.getString(2), tl.updated,\n\t\t\t\t\t\t\tc.getString(3));\n\t\t\t\t\trl.insert(context, db);\n\t\t\t\t}\n\t\t\t}\n\t\t\tc.close();\n\n\t\t\t// Then notes\n\t\t\tif (!listIDMap.isEmpty()) {\n\t\t\t\t// query\n\t\t\t\tc = getLegacyNotes(legacyDB);\n\n\t\t\t\t// iterate over notes\n\t\t\t\twhile (!c.isClosed() && c.moveToNext()) {\n\t\t\t\t\tTask t = new Task();\n\t\t\t\t\tt.title = c.getString(1);\n\t\t\t\t\tt.note = c.getString(2);\n\n\t\t\t\t\tif (t.note.contains(\"[locked]\")) {\n\t\t\t\t\t\tt.locked = true;\n\t\t\t\t\t\tt.note = t.note.replace(\"[locked]\", \"\");\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tt.due = RFC3339Date\n\t\t\t\t\t\t\t\t.parseRFC3339Date(c.getString(3))\n\t\t\t\t\t\t\t\t.getTime();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tNnnLogger.warning(DatabaseHandler.class, \"date error\");\n\t\t\t\t\t}\n\n\t\t\t\t\t// completed must be converted\n\t\t\t\t\tif (c.getString(4) != null\n\t\t\t\t\t\t\t&& \"completed\".equals(c.getString(4))) {\n\t\t\t\t\t\tt.setAsCompletedForLegacy();\n\t\t\t\t\t}\n\t\t\t\t\tt.dblist = listIDMap.get(c.getLong(5));\n\n\t\t\t\t\tt.updated = c.getLong(6);\n\n\t\t\t\t\t// insert\n\t\t\t\t\t// Just make extra sure list exists\n\t\t\t\t\tif (t.dblist != null) {\n\t\t\t\t\t\tt.insert(context, db);\n\n\t\t\t\t\t\t// put in idmap\n\t\t\t\t\t\ttaskIDMap.put(c.getLong(0), t._id);\n\t\t\t\t\t}\n\n\t\t\t\t\t// gtask\n\t\t\t\t\tGoogleTask gt;\n\t\t\t\t\tif (!c.isNull(7)\n\t\t\t\t\t\t\t&& !c.getString(7).isEmpty()\n\t\t\t\t\t\t\t&& !c.isNull(8)\n\t\t\t\t\t\t\t&& !c.getString(8).isEmpty()) {\n\t\t\t\t\t\tgt = new GoogleTask(t, c.getString(8));\n\t\t\t\t\t\tgt.remoteId = c.getString(7);\n\t\t\t\t\t\tgt.updated = t.updated;\n\t\t\t\t\t\tgt.insert(context, db);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\tc.close();\n\t\t\t}\n\n\t\t\t// Then notifications\n\t\t\tif (!taskIDMap.isEmpty()) {\n\t\t\t\tc = getLegacyNotifications(legacyDB);\n\n\t\t\t\twhile (!c.isClosed() && c.moveToNext()) {\n\t\t\t\t\t// Make sure id exists\n\t\t\t\t\tif (taskIDMap.containsValue(c.getLong(2))) {\n\t\t\t\t\t\tvar n = new Notification(taskIDMap.get(c.getLong(2)));\n\t\t\t\t\t\tn.time = c.getLong(0);\n\t\t\t\t\t\t// permanent was not supported at the time\n\t\t\t\t\t\t// insert\n\t\t\t\t\t\tn.insert(context, db);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tc.close();\n\t\t\t}\n\n\t\t\t// Complete, close the legacy db\n\t\t\tlegacyDB.close();\n\t\t} catch (SQLException e) {\n\t\t\t// Database must have been empty. Ignore it\n\t\t}\n\n\t\t// ------------\n\t\t// If no lists, insert a list and example note.\n\t\t// ------------\n\n\t\tCursor c = db.query(TaskList.TABLE_NAME, TaskList.Columns.FIELDS, null,\n\t\t\t\tnull, null, null, null);\n\n\t\tif (!c.isClosed() && c.getCount() > 0) {\n\t\t\t// there is already a database: don't add anything\n\t\t} else {\n\t\t\t// there wasn't a database: add a new list (called \"tasks\" in the user's language)\n\t\t\tfinal TaskList tl = new TaskList();\n\t\t\ttl.title = context.getString(R.string.tasks);\n\t\t\ttl.insert(context, db);\n\n\t\t\t// compose a note that is shown when the app is first installed\n\t\t\tString welcomeNoteText =\n\t\t\t\t\t// first the title. the \\n separates it from the content\n\t\t\t\t\tcontext.getString(R.string.welcome_note_title) + \"\\n\"\n\t\t\t\t\t\t\t// an hint visible also on the task list\n\t\t\t\t\t\t\t+ context.getString(R.string.welcome_note_row_2) + \"\\n\\n\\n\"\n\t\t\t\t\t\t\t// when the user open the task, he is told to open the tutorial\n\t\t\t\t\t\t\t+ context.getString(R.string.welcome_note_row_3) + \" \"\n\t\t\t\t\t\t\t+ Constants.TUTORIAL_URL;\n\n\t\t\tfinal Task task = new Task();\n\t\t\ttask.setText(welcomeNoteText);\n\t\t\ttask.dblist = tl._id;\n\t\t\ttry {\n\t\t\t\ttask.insert(context, db);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// well, whatever, the note will not be added. I'm sure the user will find the\n\t\t\t\t// tutorial anyway...\n\t\t\t\tNnnLogger.exception(e);\n\t\t\t}\n\t\t}\n\t\tc.close();\n\t\tdb.setTransactionSuccessful();\n\t\tdb.endTransaction();\n\t}\n\n\t@Override\n\tpublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n\t\tif (oldVersion < 10) {\n\t\t\t// Notification locations\n\t\t\t// Add columns\n\t\t\tString preName = \"ALTER TABLE \" + Notification.TABLE_NAME + \" ADD COLUMN \";\n\t\t\tString postText = \" TEXT\";\n\t\t\tString postReal = \" REAL\";\n\t\t\tdb.execSQL(preName + Notification.Columns.LOCATIONNAME + postText);\n\t\t\tdb.execSQL(preName + Notification.Columns.LATITUDE + postReal);\n\t\t\tdb.execSQL(preName + Notification.Columns.LONGITUDE + postReal);\n\t\t\tdb.execSQL(preName + Notification.Columns.RADIUS + postReal);\n\t\t\t// Drop view\n\t\t\tdb.execSQL(\"DROP VIEW IF EXISTS \" + Notification.WITH_TASK_VIEW_NAME);\n\t\t\t// Recreate view with additional tables\n\t\t\tdb.execSQL(Notification.CREATE_JOINED_VIEW);\n\t\t}\n\t\tif (oldVersion < 11) {\n\t\t\t// Mark as deleted when real item deleted\n\t\t\tdb.execSQL(RemoteTask.TRIGGER_REALDELETE_MARK);\n\t\t\tdb.execSQL(RemoteTaskList.TRIGGER_REALDELETE_MARK);\n\t\t}\n\t\tif (oldVersion < 12) {\n\t\t\t// Recreate trigger\n\t\t\tdb.execSQL(\"DROP TRIGGER IF EXISTS task_post_delete\");\n\t\t\tdb.execSQL(Task.TRIGGER_POST_DELETE);\n\t\t}\n\t\tif (oldVersion < 13) {\n\t\t\t// Create move list trigger\n\t\t\tdb.execSQL(RemoteTask.TRIGGER_MOVE_LIST);\n\t\t\t// Create trigger to fix positions when moving lists\n\t\t\tdb.execSQL(Task.TRIGGER_MOVE_LIST);\n\t\t}\n\t\tif (oldVersion < 14) {\n\t\t\t// Update history update trigger\n\t\t\tdb.execSQL(\"DROP TRIGGER IF EXISTS \" + Task.HISTORY_UPDATE_TRIGGER_NAME);\n\t\t\tdb.execSQL(Task.CREATE_HISTORY_UPDATE_TRIGGER);\n\t\t}\n\t\tif (oldVersion < 15) {\n\t\t\t// Drop view, changing to temporary view instead\n\t\t\tdb.execSQL(\"DROP VIEW IF EXISTS \" + Notification.WITH_TASK_VIEW_NAME);\n\t\t}\n\t\t// TODO if you want to change the database, add code here to handle the upgrade!\n\t}\n\n\t/**\n\t * Used by Espresso tests to remove the whole database\n\t * when cleaning up after tests\n\t */\n\tpublic static void resetDatabase(Context context) {\n\t\tcontext.deleteDatabase(DatabaseHandler.DATABASE_NAME);\n\t\tsingleton = new DatabaseHandler(context);\n\t\tDatabaseHandler.getInstance(context).getWritableDatabase();\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/LegacyDBHelper.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.app.SearchManager;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\nimport android.util.Log;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.RFC3339Date;\n\n/**\n * This class contains the code that has been called over the versions to\n * upgrade the database. Upgrades should be saved here as plain text to enable a\n * linear progression from 1.0 to current version without problems even if the\n * entire database is changed.\n *\n * onUpgrade should be called first from the databaseopenhelper's onUpgrade\n * method.\n */\npublic class LegacyDBHelper extends SQLiteOpenHelper {\n\n\tpublic static final String LEGACY_DATABASE_NAME = \"note_pad.db\";\n\tpublic static final int LEGACY_DATABASE_FINAL_VERSION = 8;\n\n\tpublic LegacyDBHelper(Context context) {\n\t\tthis(context, \"\");\n\t}\n\n\tpublic LegacyDBHelper(Context context, String testPrefix) {\n\t\tsuper(context.getApplicationContext(), testPrefix\n\t\t\t\t+ LEGACY_DATABASE_NAME, null, LEGACY_DATABASE_FINAL_VERSION);\n\t}\n\n\t@Override\n\tpublic void onCreate(SQLiteDatabase db) {\n\t\t// Don't create anything if the database doesn't exist before.\n\t}\n\n\tpublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n\t\tNnnLogger.debug(LegacyDBHelper.class,\n\t\t\t\t\"onUpgrade \" + \"Upgrading database from version \" + oldVersion + \" to \" + newVersion);\n\n\t\tif (oldVersion < 3) {\n\t\t\t// FIrst add columns to Notes table\n\n\t\t\tString preName = \"ALTER TABLE \" + \"notes\" + \" ADD COLUMN \";\n\t\t\t// Don't want null values. Prefer empty String\n\t\t\tString postText = \" TEXT\";\n\t\t\tString postNameInt = \" INTEGER\";\n\t\t\t// Add Columns to Notes DB\n\t\t\tdb.execSQL(preName + \"list\" + postNameInt);\n\t\t\tdb.execSQL(preName + \"duedate\" + postText);\n\t\t\tdb.execSQL(preName + \"gtaskstatus\" + postText);\n\t\t\tdb.execSQL(preName + \"modifiedflag\" + postNameInt);\n\t\t\tdb.execSQL(preName + \"deleted\" + postNameInt);\n\n\t\t\t// Then create the 3 missing tables\n\t\t\tdb.execSQL(\"CREATE TABLE \" + \"lists\" + \" (\" + BaseColumns._ID\n\t\t\t\t\t+ \" INTEGER PRIMARY KEY,\" + \"title\"\n\t\t\t\t\t+ \" TEXT DEFAULT '' NOT NULL,\" + \"modifiedflag\"\n\t\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\" + \"modified\"\n\t\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL,\" + \"deleted\"\n\t\t\t\t\t+ \" INTEGER DEFAULT 0 NOT NULL\" + \");\");\n\n\t\t\tdb.execSQL(\"CREATE TABLE \" + \"gtasks\" + \" (\" + BaseColumns._ID\n\t\t\t\t\t+ \" INTEGER PRIMARY KEY,\" + \"dbid\"\n\t\t\t\t\t+ \" INTEGER UNIQUE NOT NULL REFERENCES \" + \"notes\" + \",\"\n\t\t\t\t\t+ \"googleid\" + \" INTEGER NOT NULL,\" + \"googleaccount\"\n\t\t\t\t\t+ \" INTEGER NOT NULL,\" + \"updated\" + \" TEXT,\" + \"etag\"\n\t\t\t\t\t+ \" TEXT\" + \");\");\n\n\t\t\tdb.execSQL(\"CREATE TABLE \" + \"gtasklists\" + \" (\" + BaseColumns._ID\n\t\t\t\t\t+ \" INTEGER PRIMARY KEY,\" + \"dbid\"\n\t\t\t\t\t+ \" INTEGER UNIQUE NOT NULL REFERENCES \" + \"lists\" + \",\"\n\t\t\t\t\t+ \"googleid\" + \" INTEGER NOT NULL,\" + \"googleaccount\"\n\t\t\t\t\t+ \" INTEGER NOT NULL,\" + \"updated\" + \" TEXT,\" + \"etag\"\n\t\t\t\t\t+ \" TEXT\" + \");\");\n\n\t\t\t// Now insert a default list\n\t\t\tContentValues values = new ContentValues();\n\t\t\tvalues.put(\"title\", \"Tasks\");\n\t\t\tvalues.put(\"modifiedflag\", 1);\n\t\t\tvalues.put(\"deleted\", 0);\n\t\t\tlong listId = db.insert(\"lists\", null, values);\n\n\t\t\t// Place all existing notes in this list\n\t\t\t// And give them sensible values in the new columns\n\t\t\tvalues.clear();\n\t\t\tvalues.put(\"list\", listId);\n\t\t\tvalues.put(\"modifiedflag\", 1);\n\t\t\tvalues.put(\"deleted\", 0);\n\t\t\tvalues.put(\"duedate\", \"\");\n\t\t\tvalues.put(\"gtaskstatus\", \"needsAction\");\n\n\t\t\tdb.update(\"notes\", values, \"list\" + \" IS NOT ?\",\n\t\t\t\t\tnew String[] { Long.toString(listId) });\n\t\t}\n\t\tif (oldVersion < 4) {\n\n\t\t\tString preName = \"ALTER TABLE \" + \"notes\" + \" ADD COLUMN \";\n\t\t\tString postText = \" TEXT\";\n\t\t\tString postNameInt = \" INTEGER\";\n\t\t\t// Add Columns to Notes DB\n\t\t\tdb.execSQL(preName + \"gtasks_parent\" + postText);\n\t\t\tdb.execSQL(preName + \"gtasks_position\" + postText);\n\t\t\tdb.execSQL(preName + \"hiddenflag\" + postNameInt);\n\n\t\t\t// Give all notes sensible values\n\t\t\tContentValues values = new ContentValues();\n\t\t\tvalues.put(\"gtasks_parent\", \"\");\n\t\t\tvalues.put(\"gtasks_position\", \"\");\n\t\t\tvalues.put(\"hiddenflag\", 0);\n\t\t\tdb.update(\"notes\", values, \"hiddenflag\" + \" IS NOT ?\",\n\t\t\t\t\tnew String[] { \"0\" });\n\t\t}\n\t\tif (oldVersion < 5) {\n\n\t\t\tString preName = \"ALTER TABLE \" + \"notes\" + \" ADD COLUMN \";\n\t\t\tString postText = \" TEXT DEFAULT ''\";\n\t\t\tString postNameInt = \" INTEGER DEFAULT 0\";\n\t\t\tdb.execSQL(preName + \"possubsort\" + postText);\n\t\t\tdb.execSQL(preName + \"localhidden\" + postNameInt);\n\t\t}\n\t\tif (oldVersion < 6) {\n\t\t\t// Add Columns to Notes DB\n\t\t\tString preName = \"ALTER TABLE \" + \"notes\" + \" ADD COLUMN \";\n\t\t\tString postNameInt = \" INTEGER DEFAULT 0\";\n\t\t\tdb.execSQL(preName + \"indentlevel\" + postNameInt);\n\t\t\tdb.execSQL(preName + \"locked\" + postNameInt);\n\n\t\t\t// Mark all notes as modified to ensure we set the indents on\n\t\t\t// next sync\n\t\t\tContentValues values = new ContentValues();\n\t\t\tvalues.put(\"modifiedflag\", 1);\n\t\t\tdb.update(\"notes\", values, null, null);\n\t\t}\n\t\tif (oldVersion < 7) {\n\t\t\tdb.execSQL(\"CREATE TABLE \" + \"notification\" + \" (\"\n\t\t\t\t\t+ BaseColumns._ID + \" INTEGER PRIMARY KEY,\" + \"time\"\n\t\t\t\t\t+ \" INTEGER NOT NULL DEFAULT 0,\" + \"permanent\"\n\t\t\t\t\t+ \" INTEGER NOT NULL DEFAULT 0,\" + \"noteid\" + \" INTEGER,\"\n\t\t\t\t\t+ \"FOREIGN KEY(\" + \"noteid\" + \") REFERENCES \" + \"notes\"\n\t\t\t\t\t+ \"(\" + BaseColumns._ID + \") ON DELETE CASCADE\" + \");\");\n\t\t}\n\t\tif (oldVersion < 8) {\n\t\t\ttry {\n\t\t\t\tdb.execSQL(\"CREATE TRIGGER post_note_markdelete AFTER UPDATE ON \"\n\t\t\t\t\t\t+ \"notes\"\n\t\t\t\t\t\t+ \" WHEN new.\"\n\t\t\t\t\t\t+ \"deleted\"\n\t\t\t\t\t\t+ \" = 1\"\n\t\t\t\t\t\t+ \" BEGIN\"\n\t\t\t\t\t\t+ \"   DELETE FROM \"\n\t\t\t\t\t\t+ \"notification\"\n\t\t\t\t\t\t+ \"   WHERE \"\n\t\t\t\t\t\t+ \"notification\"\n\t\t\t\t\t\t+ \".\"\n\t\t\t\t\t\t+ \"noteid\"\n\t\t\t\t\t\t+ \"   = \" + \"new.\" + BaseColumns._ID + \";\" + \" END\");\n\t\t\t} catch (SQLException e) {\n\t\t\t\tLog.d(\"NNN\", \"Creating trigger failed. It probably already existed:\");\n\t\t\t\tNnnLogger.exception(e);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tdb.execSQL(\"CREATE TRIGGER post_note_actualdelete AFTER DELETE ON \"\n\t\t\t\t\t\t+ \"notes\"\n\t\t\t\t\t\t+ \" BEGIN\"\n\t\t\t\t\t\t+ \"   DELETE FROM \"\n\t\t\t\t\t\t+ \"notification\"\n\t\t\t\t\t\t+ \"   WHERE \"\n\t\t\t\t\t\t+ \"notification\"\n\t\t\t\t\t\t+ \".\"\n\t\t\t\t\t\t+ \"noteid\"\n\t\t\t\t\t\t+ \"   = \"\n\t\t\t\t\t\t+ \"old.\"\n\t\t\t\t\t\t+ BaseColumns._ID\n\t\t\t\t\t\t+ \";\"\n\t\t\t\t\t\t+ \" END\");\n\t\t\t} catch (SQLException e) {\n\t\t\t\tNnnLogger.exception(e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static final class NotePad {\n\t\tpublic static final String AUTHORITY = MyContentProvider.AUTHORITY;\n\n\t\t// This class cannot be instantiated\n\t\tprivate NotePad() {\n\t\t}\n\n\t\t/**\n\t\t * Notes table contract\n\t\t */\n\t\tpublic static final class Notes implements BaseColumns {\n\n\t\t\t// This class cannot be instantiated\n\t\t\tprivate Notes() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * The table name offered by this provider\n\t\t\t */\n\t\t\tpublic static final String TABLE_NAME = \"notes\";\n\t\t\tpublic static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;\n\n\t\t\t/*\n\t\t\t * URI definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The scheme part for this provider's URI\n\t\t\t */\n\t\t\tprivate static final String SCHEME = \"content://\";\n\n\t\t\t// -----------------------\n\t\t\t// Path parts for the URIs\n\t\t\t// -----------------------\n\n\t\t\t/**\n\t\t\t * Path part for the Notes URI\n\t\t\t */\n\t\t\tpublic static final String PATH_NOTES = \"/notes\";\n\t\t\tpublic static final String NOTES = \"notes\";\n\t\t\t// Visible notes\n\t\t\tpublic static final String PATH_VISIBLE_NOTES = \"/visiblenotes\";\n\t\t\tpublic static final String VISIBLE_NOTES = \"visiblenotes\";\n\t\t\t// Complete note entry including stuff in GTasks table\n\t\t\tprivate static final String PATH_JOINED_NOTES = \"/joinednotes\";\n\n\t\t\t/**\n\t\t\t * Path part for the Note ID URI\n\t\t\t */\n\t\t\tpublic static final String PATH_NOTE_ID = \"/notes/\";\n\t\t\tpublic static final String PATH_VISIBLE_NOTE_ID = \"/visiblenotes/\";\n\n\t\t\t/**\n\t\t\t * 0-relative position of a note ID segment in the path part of a\n\t\t\t * note ID URI\n\t\t\t */\n\t\t\tpublic static final int NOTE_ID_PATH_POSITION = 1;\n\n\t\t\t/**\n\t\t\t * The content:// style URL for this table\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY\n\t\t\t\t\t+ PATH_NOTES);\n\t\t\tpublic static final Uri CONTENT_VISIBLE_URI = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_VISIBLE_NOTES);\n\t\t\tpublic static final Uri CONTENT_JOINED_URI = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_JOINED_NOTES);\n\n\t\t\t/**\n\t\t\t * The content URI base for a single note. Callers must append a\n\t\t\t * numeric note id to this Uri to retrieve a note\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_NOTE_ID);\n\t\t\tpublic static final Uri CONTENT_VISIBLE_ID_URI_BASE = Uri\n\t\t\t\t\t.parse(SCHEME + AUTHORITY + PATH_VISIBLE_NOTE_ID);\n\n\t\t\t/**\n\t\t\t * The content URI match pattern for a single note, specified by its\n\t\t\t * ID. Use this to match incoming URIs or to construct an Intent.\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_PATTERN = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_NOTE_ID + \"/#\");\n\t\t\tpublic static final Uri CONTENT_VISIBLE_ID_URI_PATTERN = Uri\n\t\t\t\t\t.parse(SCHEME + AUTHORITY + PATH_VISIBLE_NOTE_ID + \"/#\");\n\n\t\t\t/*\n\t\t\t * MIME type definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The MIME type of a {@link #CONTENT_URI} sub-directory of a single\n\t\t\t * note.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_ITEM_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.note\";\n\n\t\t\t/**\n\t\t\t * The MIME type of {@link #CONTENT_URI} providing a directory of\n\t\t\t * notes.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_TYPE = CONTENT_ITEM_TYPE;\n\n\t\t\t/*\n\t\t\t * Column definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * Column name for the title of the note\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_TITLE = \"title\";\n\n\t\t\t/**\n\t\t\t * Column name of the note content\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_NOTE = \"note\";\n\n\t\t\t/**\n\t\t\t * Column name for the creation timestamp\n\t\t\t * <P>\n\t\t\t * Type: INTEGER (long from System.curentTimeMillis())\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_CREATE_DATE = \"created\";\n\n\t\t\t/**\n\t\t\t * Column name for the modification timestamp\n\t\t\t * <P>\n\t\t\t * Type: INTEGER (long from System.curentTimeMillis())\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_MODIFICATION_DATE = \"modified\";\n\n\t\t\t/**\n\t\t\t * Due date of the task (as an RFC 3339 timestamp) formatted as\n\t\t\t * String.\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_DUE_DATE = \"duedate\";\n\n\t\t\t/**\n\t\t\t * Status of task, such as \"completed\"\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_GTASKS_STATUS = \"gtaskstatus\";\n\n\t\t\t/**\n\t\t\t * INTEGER, id of entry in lists table\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_LIST = \"list\";\n\n\t\t\t/**\n\t\t\t * Deleted flag\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_DELETED = \"deleted\";\n\n\t\t\t/**\n\t\t\t * Modified flag\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_MODIFIED = \"modifiedflag\";\n\n\t\t\t// parent position hidden\n\n\t\t\tpublic static final String COLUMN_NAME_PARENT = \"gtasks_parent\";\n\n\t\t\tpublic static final String COLUMN_NAME_POSITION = \"gtasks_position\";\n\t\t\tpublic static final String COLUMN_NAME_HIDDEN = \"hiddenflag\";\n\n\t\t\t// server side sorting and local hiding\n\t\t\tpublic static final String COLUMN_NAME_INDENTLEVEL = \"indentlevel\";\n\t\t\tpublic static final String COLUMN_NAME_POSSUBSORT = \"possubsort\";\n\t\t\tpublic static final String COLUMN_NAME_LOCALHIDDEN = \"localhidden\";\n\n\n\t\t\tpublic static final String ALPHABETIC_SORT_TYPE = COLUMN_NAME_TITLE\n\t\t\t\t\t+ \" COLLATE NOCASE\";\n\t\t\t// We want items with no due dates to be placed at the end, hence the sql magic\n\t\t\t// Coalesce returns the first non-null argument\n\t\t\tpublic static final String MODIFICATION_SORT_TYPE = COLUMN_NAME_MODIFICATION_DATE;\n\t\t\tpublic static final String DUEDATE_SORT_TYPE = \"CASE WHEN \"\n\t\t\t\t\t+ COLUMN_NAME_DUE_DATE + \" IS NULL OR \"\n\t\t\t\t\t+ COLUMN_NAME_DUE_DATE + \" IS '' THEN 1 ELSE 0 END, \"\n\t\t\t\t\t+ COLUMN_NAME_DUE_DATE;\n\t\t\tpublic static final String POSSUBSORT_SORT_TYPE = COLUMN_NAME_POSSUBSORT;\n\n\t\t\tpublic static final String ASCENDING_SORT_ORDERING = \"ASC\";\n\t\t\tpublic static final String DESCENDING_SORT_ORDERING = \"DESC\";\n\t\t\tpublic static final String ALPHABETIC_ASC_ORDER = COLUMN_NAME_TITLE\n\t\t\t\t\t+ \" COLLATE NOCASE ASC\";\n\n\t\t\t/**\n\t\t\t * The default sort order for this table\n\t\t\t */\n\t\t\tpublic static final String DEFAULT_SORT_TYPE = POSSUBSORT_SORT_TYPE;\n\t\t\tpublic static final String DEFAULT_SORT_ORDERING = ASCENDING_SORT_ORDERING;\n\n\t\t\tpublic static String SORT_ORDER = ALPHABETIC_ASC_ORDER;\n\t\t}\n\n\t\t/**\n\t\t * Lists table contract\n\t\t */\n\t\tpublic static final class Lists implements BaseColumns {\n\n\t\t\t// This class cannot be instantiated\n\t\t\tprivate Lists() {\n\t\t\t}\n\n\t\t\tpublic static final String DEFAULT_LIST_NAME = \"Notes\";\n\n\t\t\t/**\n\t\t\t * The table name offered by this provider\n\t\t\t */\n\t\t\tpublic static final String TABLE_NAME = \"lists\";\n\t\t\tpublic static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;\n\n\t\t\t/*\n\t\t\t * URI definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The scheme part for this provider's URI\n\t\t\t */\n\t\t\tprivate static final String SCHEME = \"content://\";\n\n\t\t\t// -----------------------\n\t\t\t// Path parts for the URIs\n\t\t\t// -----------------------\n\n\t\t\t/**\n\t\t\t * Path part for the Lists URI\n\t\t\t */\n\t\t\tpublic static final String PATH_LISTS = \"/lists\";\n\t\t\tpublic static final String LISTS = \"lists\";\n\t\t\tpublic static final String PATH_VISIBLE_LISTS = \"/visiblelists\";\n\t\t\tpublic static final String VISIBLE_LISTS = \"visiblelists\";\n\t\t\t// Complete entry gotten with a join with GTasksLists table\n\t\t\tprivate static final String PATH_JOINED_LISTS = \"/joinedlists\";\n\n\t\t\t/**\n\t\t\t * Path part for the List ID URI\n\t\t\t */\n\t\t\tpublic static final String PATH_LIST_ID = \"/lists/\";\n\t\t\tpublic static final String PATH_VISIBLE_LIST_ID = \"/visiblelists/\";\n\n\t\t\t/**\n\t\t\t * 0-relative position of a ID segment in the path part of a ID URI\n\t\t\t */\n\t\t\tpublic static final int ID_PATH_POSITION = 1;\n\n\t\t\t/**\n\t\t\t * The content:// style URL for this table\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY\n\t\t\t\t\t+ PATH_LISTS);\n\t\t\tpublic static final Uri CONTENT_VISIBLE_URI = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_VISIBLE_LISTS);\n\t\t\tpublic static final Uri CONTENT_JOINED_URI = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_JOINED_LISTS);\n\n\t\t\t/**\n\t\t\t * The content URI base for a single note. Callers must append a\n\t\t\t * numeric note id to this Uri to retrieve a note\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_LIST_ID);\n\t\t\tpublic static final Uri CONTENT_VISIBLE_ID_URI_BASE = Uri\n\t\t\t\t\t.parse(SCHEME + AUTHORITY + PATH_VISIBLE_LIST_ID);\n\n\t\t\t/**\n\t\t\t * The content URI match pattern for a single note, specified by its\n\t\t\t * ID. Use this to match incoming URIs or to construct an Intent.\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_PATTERN = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_LIST_ID + \"/#\");\n\t\t\tpublic static final Uri CONTENT_VISIBLE_ID_URI_PATTERN = Uri\n\t\t\t\t\t.parse(SCHEME + AUTHORITY + PATH_VISIBLE_LIST_ID + \"/#\");\n\n\t\t\t/*\n\t\t\t * MIME type definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The MIME type of a {@link #CONTENT_URI} sub-directory of a single\n\t\t\t * item.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_ITEM_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.list\";\n\n\t\t\t/**\n\t\t\t * The MIME type of {@link #CONTENT_URI} providing a directory.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_TYPE = CONTENT_ITEM_TYPE;\n\n\t\t\t/*\n\t\t\t * Column definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * Column name for the title of the note\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_TITLE = \"title\";\n\n\t\t\t/**\n\t\t\t * Deleted flag\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_DELETED = \"deleted\";\n\n\t\t\t/**\n\t\t\t * Modified flag\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_MODIFIED = \"modifiedflag\";\n\n\t\t\t/**\n\t\t\t * Column name for the modification timestamp\n\t\t\t * <P>\n\t\t\t * Type: INTEGER (long from System.curentTimeMillis())\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_MODIFICATION_DATE = \"modified\";\n\n\t\t\t/**\n\t\t\t * The default sort order for this table\n\t\t\t */\n\n\t\t\tpublic static final String DEFAULT_SORT_TYPE = COLUMN_NAME_MODIFICATION_DATE;\n\t\t\tpublic static final String DEFAULT_SORT_ORDERING = \"DESC\";\n\t\t\tpublic static final String MODIFIED_DESC_ORDER = COLUMN_NAME_MODIFICATION_DATE\n\t\t\t\t\t+ \" DESC\";\n\t\t\tpublic static final String ALPHABETIC_ASC_ORDER = COLUMN_NAME_TITLE\n\t\t\t\t\t+ \" COLLATE NOCASE ASC\";\n\n\t\t\tpublic static String SORT_ORDER = ALPHABETIC_ASC_ORDER;\n\t\t}\n\n\t\t/**\n\t\t * GoogleTasks table contract\n\t\t */\n\t\tpublic static final class GTasks implements BaseColumns {\n\n\t\t\t// This class cannot be instantiated\n\t\t\tprivate GTasks() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * The table name offered by this provider\n\t\t\t */\n\t\t\tpublic static final String TABLE_NAME = \"gtasks\";\n\t\t\tpublic static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;\n\n\t\t\t/*\n\t\t\t * URI definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The scheme part for this provider's URI\n\t\t\t */\n\t\t\tprivate static final String SCHEME = \"content://\";\n\n\t\t\t// -----------------------\n\t\t\t// Path parts for the URIs\n\t\t\t// -----------------------\n\n\t\t\t/**\n\t\t\t * Path part for the Lists URI\n\t\t\t */\n\t\t\tprivate static final String PATH = \"/gtasks\";\n\n\t\t\t/**\n\t\t\t * Path part for the List ID URI\n\t\t\t */\n\t\t\tprivate static final String PATH_ID = \"/gtasks/\";\n\n\t\t\t/**\n\t\t\t * 0-relative position of a note ID segment in the path part of a\n\t\t\t * note ID URI\n\t\t\t */\n\t\t\tpublic static final int ID_PATH_POSITION = 1;\n\n\t\t\t/**\n\t\t\t * The content:// style URL for this table\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY\n\t\t\t\t\t+ PATH);\n\n\t\t\t/**\n\t\t\t * The content URI base for a single note. Callers must append a\n\t\t\t * numeric note id to this Uri to retrieve a note\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_ID);\n\n\t\t\t/**\n\t\t\t * The content URI match pattern for a single note, specified by its\n\t\t\t * ID. Use this to match incoming URIs or to construct an Intent.\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_PATTERN = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_ID + \"/#\");\n\n\t\t\t/*\n\t\t\t * MIME type definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The MIME type of {@link #CONTENT_URI} providing a directory of\n\t\t\t * notes.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.dir/vnd.nononsenseapps.gtask\";\n\n\t\t\t/**\n\t\t\t * The MIME type of a {@link #CONTENT_URI} sub-directory of a single\n\t\t\t * note.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_ITEM_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.gtask\";\n\n\t\t\t/*\n\t\t\t * Column definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: INTEGER, database ID\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_DB_ID = \"dbid\";\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_GTASKS_ID = \"googleid\";\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_GOOGLE_ACCOUNT = \"googleaccount\";\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_ETAG = \"etag\";\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_UPDATED = \"updated\";\n\t\t}\n\n\t\t/**\n\t\t * GoogleTaskLists table contract\n\t\t */\n\t\tpublic static final class GTaskLists implements BaseColumns {\n\n\t\t\t// This class cannot be instantiated\n\t\t\tprivate GTaskLists() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * The table name offered by this provider\n\t\t\t */\n\t\t\tpublic static final String TABLE_NAME = \"gtasklists\";\n\t\t\tpublic static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;\n\n\t\t\t/*\n\t\t\t * URI definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The scheme part for this provider's URI\n\t\t\t */\n\t\t\tprivate static final String SCHEME = \"content://\";\n\n\t\t\t// -----------------------\n\t\t\t// Path parts for the URIs\n\t\t\t// -----------------------\n\n\t\t\t/**\n\t\t\t * Path part for the Lists URI\n\t\t\t */\n\t\t\tprivate static final String PATH = \"/gtasklists\";\n\n\t\t\t/**\n\t\t\t * Path part for the List ID URI\n\t\t\t */\n\t\t\tprivate static final String PATH_ID = \"/gtasklists/\";\n\n\t\t\t/**\n\t\t\t * 0-relative position of a note ID segment in the path part of a\n\t\t\t * note ID URI\n\t\t\t */\n\t\t\tpublic static final int ID_PATH_POSITION = 1;\n\n\t\t\t/**\n\t\t\t * The content:// style URL for this table\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY\n\t\t\t\t\t+ PATH);\n\n\t\t\t/**\n\t\t\t * The content URI base for a single note. Callers must append a\n\t\t\t * numeric note id to this Uri to retrieve a note\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_ID);\n\n\t\t\t/**\n\t\t\t * The content URI match pattern for a single note, specified by its\n\t\t\t * ID. Use this to match incoming URIs or to construct an Intent.\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_PATTERN = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_ID + \"/#\");\n\n\t\t\t/*\n\t\t\t * MIME type definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The MIME type of {@link #CONTENT_URI} providing a directory of\n\t\t\t * notes.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.dir/vnd.nononsenseapps.gtasklist\";\n\n\t\t\t/**\n\t\t\t * The MIME type of a {@link #CONTENT_URI} sub-directory of a single\n\t\t\t * note.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_ITEM_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.gtasklist\";\n\n\t\t\t/*\n\t\t\t * Column definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: INTEGER, database ID\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_DB_ID = \"dbid\";\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_GTASKS_ID = \"googleid\";\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_GOOGLE_ACCOUNT = \"googleaccount\";\n\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_ETAG = \"etag\";\n\t\t\t/**\n\t\t\t * <P>\n\t\t\t * Type: TEXT\n\t\t\t * </P>\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_UPDATED = \"updated\";\n\t\t}\n\n\t\t/**\n\t\t * Notifications table contract\n\t\t */\n\t\tpublic static final class Notifications implements BaseColumns {\n\n\t\t\t// This class cannot be instantiated\n\t\t\tprivate Notifications() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * The table name offered by this provider\n\t\t\t */\n\t\t\tpublic static final String TABLE_NAME = \"notification\";\n\t\t\tpublic static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;\n\n\t\t\t/*\n\t\t\t * Column definitions\n\t\t\t */\n\t\t\tpublic static final String COLUMN_NAME_TIME = \"time\";\n\t\t\tpublic static final String COLUMN_NAME_PERMANENT = \"permanent\";\n\t\t\tpublic static final String COLUMN_NAME_NOTEID = \"noteid\";\n\n\t\t\tpublic static final String JOINED_COLUMN_LIST_TITLE = NotePad.Lists.TABLE_NAME\n\t\t\t\t\t+ \".\" + NotePad.Lists.COLUMN_NAME_TITLE;\n\n\t\t\t/*\n\t\t\t * URI definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The scheme part for this provider's URI\n\t\t\t */\n\t\t\tprivate static final String SCHEME = \"content://\";\n\n\t\t\t// -----------------------\n\t\t\t// Path parts for the URIs\n\t\t\t// -----------------------\n\n\t\t\t/**\n\t\t\t * Path part for the Lists URI\n\t\t\t */\n\t\t\tprivate static final String PATH = \"/\" + TABLE_NAME;\n\n\t\t\t/**\n\t\t\t * Path part for the List ID URI\n\t\t\t */\n\t\t\tprivate static final String PATH_ID = PATH + \"/\";\n\n\t\t\t/**\n\t\t\t * 0-relative position of a note ID segment in the path part of a\n\t\t\t * note ID URI\n\t\t\t */\n\t\t\tpublic static final int ID_PATH_POSITION = 1;\n\n\t\t\tprivate static final String PATH_JOINED_NOTIFICATIONS = \"/joinednotifications\";\n\t\t\tpublic static final String PATH_NOTIFICATIONS_LISTID = \"/notificationlists\";\n\t\t\tprivate static final String PATH_NOTIFICATIONS_LISTID_BASE = PATH_NOTIFICATIONS_LISTID\n\t\t\t\t\t+ \"/\";\n\t\t\t/**\n\t\t\t * The content:// style URL for this table\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY\n\t\t\t\t\t+ PATH);\n\n\t\t\tpublic static final Uri CONTENT_JOINED_URI = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_JOINED_NOTIFICATIONS);\n\n\t\t\tpublic static final Uri CONTENT_LISTID_URI_BASE = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_NOTIFICATIONS_LISTID);\n\n\t\t\t/**\n\t\t\t * The content URI base for a single note. Callers must append a\n\t\t\t * numeric note id to this Uri to retrieve a note\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_ID);\n\n\t\t\t/**\n\t\t\t * The content URI match pattern for a single note, specified by its\n\t\t\t * ID. Use this to match incoming URIs or to construct an Intent.\n\t\t\t */\n\t\t\tpublic static final Uri CONTENT_ID_URI_PATTERN = Uri.parse(SCHEME\n\t\t\t\t\t+ AUTHORITY + PATH_ID + \"/#\");\n\n\t\t\t/*\n\t\t\t * MIME type definitions\n\t\t\t */\n\n\t\t\t/**\n\t\t\t * The MIME type of {@link #CONTENT_URI} providing a directory of\n\t\t\t * notes.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.dir/vnd.nononsenseapps.\"\n\t\t\t\t\t+ TABLE_NAME;\n\n\t\t\t/**\n\t\t\t * The MIME type of a {@link #CONTENT_URI} sub-directory of a single\n\t\t\t * note.\n\t\t\t */\n\t\t\tpublic static final String CONTENT_ITEM_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.\"\n\t\t\t\t\t+ TABLE_NAME;\n\t\t}\n\t}\n\n\t/**\n\t * Converts the columns names from the legacy URIs. However, the data must\n\t * also be returned correctly!\n\t */\n\tpublic static String[] convertLegacyColumns(final String[] legacyCols) {\n\t\tString[] newCols = new String[legacyCols.length];\n\t\tfor (int i = 0; i < legacyCols.length; i++) {\n\t\t\tString col = legacyCols[i];\n\t\t\tString newCol = col;\n\t\t\t// Lists\n\t\t\tif (NotePad.Lists.COLUMN_NAME_TITLE.equals(col)) {\n\t\t\t\tnewCol = TaskList.Columns.TITLE;\n\t\t\t}\n\t\t\t// Tasks\n\t\t\telse if (NotePad.Notes.COLUMN_NAME_TITLE.equals(col)) {\n\t\t\t\tnewCol = Task.Columns.TITLE;\n\t\t\t} else if (NotePad.Notes.COLUMN_NAME_NOTE.equals(col)) {\n\t\t\t\tnewCol = Task.Columns.NOTE;\n\t\t\t} else if (NotePad.Notes.COLUMN_NAME_LIST.equals(col)) {\n\t\t\t\tnewCol = Task.Columns.DBLIST;\n\t\t\t} else if (NotePad.Notes.COLUMN_NAME_DUE_DATE.equals(col)) {\n\t\t\t\tnewCol = Task.Columns.DUE;\n\t\t\t} else if (NotePad.Notes.COLUMN_NAME_GTASKS_STATUS.equals(col)) {\n\t\t\t\tnewCol = Task.Columns.COMPLETED;\n\t\t\t}\n\n\t\t\t//Log.d(\"nononsenseapps db\", \"legacy converted field:\" + newCol);\n\t\t\tnewCols[i] = newCol;\n\t\t}\n\t\treturn newCols;\n\t}\n\n\t/**\n\t * Convert new values to old, but using old or new column names\n\t *\n\t * TaskProjection: new String[] { \"_id\", \"title\", \"note\", \"list\", \"duedate\",\n\t * \"gtaskstatus\"};\n\t */\n\tpublic static Object[] convertLegacyTaskValues(final Cursor cursor) {\n\t\tObject[] retval = new Object[cursor.getColumnCount()];\n\t\tfor (int i = 0; i < cursor.getColumnCount(); i++) {\n\t\t\tfinal String colName = cursor.getColumnName(i);\n\t\t\tfinal Object val;\n\t\t\tif (NotePad.Notes.COLUMN_NAME_DUE_DATE.equals(colName) ||\n\t\t\t\t\tTask.Columns.DUE.equals(colName)) {\n\t\t\t\tval = cursor.isNull(i) ? \"\" : RFC3339Date.asRFC3339(cursor.getLong(i));\n\t\t\t} else if (NotePad.Notes.COLUMN_NAME_GTASKS_STATUS.equals(colName) ||\n\t\t\t\t\tTask.Columns.COMPLETED.equals(colName)) {\n\t\t\t\tval = cursor.isNull(i) ? \"needsAction\" : \"completed\";\n\t\t\t} else {\n\t\t\t\tval = switch (cursor.getType(i)) {\n\t\t\t\t\tcase Cursor.FIELD_TYPE_FLOAT -> cursor.getFloat(i);\n\t\t\t\t\tcase Cursor.FIELD_TYPE_INTEGER -> cursor.getLong(i);\n\t\t\t\t\tcase Cursor.FIELD_TYPE_STRING -> cursor.getString(i);\n\t\t\t\t\t// the legacy DB did not have BLOBs, anyway\n\t\t\t\t\tcase Cursor.FIELD_TYPE_BLOB -> null;\n\t\t\t\t\tcase Cursor.FIELD_TYPE_NULL -> null;\n\t\t\t\t\tdefault -> null;\n\t\t\t\t};\n\t\t\t}\n\t\t\t//Log.d(\"nononsenseapps db\", \"legacy notes col: \" + val);\n\t\t\tretval[i] = val;\n\t\t}\n\t\treturn retval;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/MyContentProvider.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.app.SearchManager;\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteStatement;\nimport android.net.Uri;\n\nimport androidx.annotation.NonNull;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.UpdateNotifier;\nimport com.nononsenseapps.notepad.BuildConfig;\n\nimport java.util.ArrayList;\nimport java.util.Objects;\n\npublic class MyContentProvider extends ContentProvider {\n\n\t/**\n\t * The authority of the content provider must be unique for each package (app) installed,\n\t * and it must be equal to the values used in searchable.xml and AndroidManifest.xml\n\t * This one is defined in build.gradle for each buildType. It's equal to @string/NnnAuthority_1\n\t */\n\tpublic static final String AUTHORITY = BuildConfig.APPLICATION_ID + \".MyContentAuthority\";\n\n\tpublic static final String SCHEME = \"content://\";\n\tprivate static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);\n\n\tstatic {\n\t\tTaskList.addMatcherUris(sURIMatcher);\n\t\tTask.addMatcherUris(sURIMatcher);\n\t\tNotification.addMatcherUris(sURIMatcher);\n\t\tRemoteTaskList.addMatcherUris(sURIMatcher);\n\t\tRemoteTask.addMatcherUris(sURIMatcher);\n\t}\n\n\tpublic MyContentProvider() {\n\t}\n\n\t@Override\n\tpublic String getType(@NonNull Uri uri) {\n\t\tswitch (sURIMatcher.match(uri)) {\n\t\t\tcase Notification.BASEITEMCODE:\n\t\t\tcase Notification.BASEURICODE:\n\t\t\tcase Notification.WITHTASKQUERYCODE:\n\t\t\tcase Notification.WITHTASKQUERYITEMCODE:\n\t\t\t\treturn Notification.CONTENT_TYPE;\n\t\t\tcase TaskList.BASEITEMCODE:\n\t\t\tcase TaskList.BASEURICODE:\n\t\t\tcase TaskList.LEGACYBASEITEMCODE:\n\t\t\tcase TaskList.LEGACYBASEURICODE:\n\t\t\tcase TaskList.LEGACYVISIBLEITEMCODE:\n\t\t\tcase TaskList.LEGACYVISIBLEURICODE:\n\t\t\t\treturn TaskList.CONTENT_TYPE;\n\t\t\tcase Task.BASEITEMCODE:\n\t\t\tcase Task.BASEURICODE:\n\t\t\tcase Task.SECTIONEDDATEITEMCODE:\n\t\t\tcase Task.SECTIONEDDATEQUERYCODE:\n\t\t\tcase Task.LEGACYBASEITEMCODE:\n\t\t\tcase Task.LEGACYBASEURICODE:\n\t\t\tcase Task.LEGACYVISIBLEITEMCODE:\n\t\t\tcase Task.LEGACYVISIBLEURICODE:\n\t\t\tcase Task.SEARCHCODE:\n\t\t\tcase Task.SEARCHSUGGESTIONSCODE:\n\t\t\t\treturn Task.CONTENT_TYPE;\n\t\t\tdefault:\n\t\t\t\t// throw new IllegalArgumentException(\"Unknown URI \" + uri);\n\t\t}\n\n\t\t// Legacy URIs, above didn't work for some reason\n\t\tif (uri.toString().startsWith(LegacyDBHelper.NotePad.Lists.CONTENT_URI.toString())\n\t\t\t\t|| uri.toString().startsWith(\n\t\t\t\tLegacyDBHelper.NotePad.Lists.CONTENT_VISIBLE_URI.toString())) {\n\t\t\treturn TaskList.CONTENT_TYPE;\n\t\t} else if (uri.toString().startsWith(LegacyDBHelper.NotePad.Notes.CONTENT_URI.toString())\n\t\t\t\t|| uri.toString().startsWith(\n\t\t\t\tLegacyDBHelper.NotePad.Notes.CONTENT_VISIBLE_URI.toString())) {\n\t\t\treturn Task.CONTENT_TYPE;\n\t\t}\n\n\t\tthrow new IllegalArgumentException(\"Unknown URI \" + uri);\n\t}\n\n\t@Override\n\tpublic boolean onCreate() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tsynchronized public Uri insert(@NonNull Uri uri, ContentValues values) {\n\t\tfinal SQLiteDatabase db = DatabaseHandler.getInstance(getContext())\n\t\t\t\t.getWritableDatabase();\n\n\t\tUri result = null;\n\n\t\tdb.beginTransaction();\n\t\t// Do not add legacy URIs\n\t\ttry {\n\t\t\tfinal DAO item = switch (sURIMatcher.match(uri)) {\n\t\t\t\tcase TaskList.BASEURICODE -> new TaskList(values);\n\t\t\t\tcase Task.BASEURICODE -> new Task(values);\n\t\t\t\tcase Notification.BASEURICODE, Notification.WITHTASKQUERYITEMCODE ->\n\t\t\t\t\t\tnew Notification(values);\n\t\t\t\tcase RemoteTaskList.BASEURICODE -> new RemoteTaskList(values);\n\t\t\t\tcase RemoteTask.BASEURICODE -> new RemoteTask(values);\n\t\t\t\tdefault -> throw new IllegalArgumentException(\"Faulty insertURI provided: \" + uri);\n\t\t\t};\n\n\t\t\tresult = item.insert(getContext(), db);\n\t\t\tdb.setTransactionSuccessful();\n\t\t} catch (SQLException e) {\n\t\t\t// Crap...\n\t\t} finally {\n\t\t\tdb.endTransaction();\n\t\t}\n\n\t\tif (result != null) {\n\t\t\tObjects.requireNonNull(getContext());\n\t\t\tDAO.notifyProviderOnChange(getContext(), uri);\n\t\t\tDAO.notifyProviderOnChange(getContext(), TaskList.URI_WITH_COUNT);\n\t\t\tUpdateNotifier.updateWidgets(getContext());\n\t\t\tUpdateNotifier.notifyChangeList(getContext());\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tsynchronized public int update(@NonNull Uri uri, ContentValues values,\n\t\t\t\t\t\t\t\t   String selection, String[] selectionArgs) {\n\t\tfinal SQLiteDatabase db = DatabaseHandler.getInstance(getContext())\n\t\t\t\t.getWritableDatabase();\n\t\tint result = 0;\n\t\tfinal Task t;\n\t\tfinal SQLiteStatement stmt;\n\t\tfinal String sql;\n\t\tfinal ArrayList<Uri> updateUris = new ArrayList<>();\n\t\tdb.beginTransaction();\n\n\t\ttry {\n\t\t\t// Do not add legacy URIs\n\t\t\tswitch (sURIMatcher.match(uri)) {\n\t\t\t\tcase TaskList.BASEITEMCODE:\n\t\t\t\t\tupdateUris.add(TaskList.URI);\n\t\t\t\t\tupdateUris.add(TaskList.URI_WITH_COUNT);\n\t\t\t\t\tfinal TaskList list = new TaskList(uri, values);\n\t\t\t\t\tresult += db.update(TaskList.TABLE_NAME, list.getContent(),\n\t\t\t\t\t\t\tTaskList.whereIdIs(selection),\n\t\t\t\t\t\t\tTaskList.whereIdArg(list._id, selectionArgs));\n\t\t\t\t\tbreak;\n\t\t\t\tcase Task.MOVEITEMLEFTCODE:\n\t\t\t\t\tupdateUris.add(Task.URI);\n\t\t\t\t\tt = new Task(values);\n\t\t\t\t\tsql = t.getSQLMoveItemLeft(values);\n\t\t\t\t\tif (sql != null) {\n\t\t\t\t\t\tstmt = db.compileStatement(sql);\n\t\t\t\t\t\tresult += stmt.executeUpdateDelete();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase Task.MOVEITEMRIGHTCODE:\n\t\t\t\t\tupdateUris.add(Task.URI);\n\t\t\t\t\tt = new Task(values);\n\t\t\t\t\tsql = t.getSQLMoveItemRight(values);\n\t\t\t\t\tif (sql != null) {\n\t\t\t\t\t\tstmt = db.compileStatement(sql);\n\t\t\t\t\t\tresult += stmt.executeUpdateDelete();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase Task.BASEITEMCODE:\n\t\t\t\t\tupdateUris.add(Task.URI);\n\t\t\t\t\tupdateUris.add(Task.URI_SECTIONED_BY_DATE);\n\t\t\t\t\tupdateUris.add(Task.URI_TASK_HISTORY);\n\t\t\t\t\tupdateUris.add(TaskList.URI);\n\t\t\t\t\tupdateUris.add(TaskList.URI_WITH_COUNT);\n\t\t\t\t\t// regular update\n\t\t\t\t\tt = new Task(uri, values);\n\t\t\t\t\tif (t.getContent().size() > 0) {\n\t\t\t\t\t\t// Something changed in task\n\n\t\t\t\t\t\tresult += db.update(Task.TABLE_NAME, t.getContent(),\n\t\t\t\t\t\t\t\tTask.whereIdIs(selection),\n\t\t\t\t\t\t\t\tTask.whereIdArg(t._id, selectionArgs));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase Task.BASEURICODE:\n\t\t\t\t\tupdateUris.add(Task.URI);\n\t\t\t\t\tupdateUris.add(TaskList.URI);\n\t\t\t\t\tupdateUris.add(TaskList.URI_WITH_COUNT);\n\t\t\t\t\t// Batch. No checks made\n\t\t\t\t\tresult += db.update(Task.TABLE_NAME, values, selection, selectionArgs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Notification.BASEITEMCODE:\n\t\t\t\tcase Notification.WITHTASKQUERYITEMCODE:\n\t\t\t\t\tupdateUris.add(Notification.URI);\n\t\t\t\t\tupdateUris.add(Notification.URI_WITH_TASK_PATH);\n\t\t\t\t\t// final Notification n = new Notification(uri, values);\n\t\t\t\t\tresult += db.update(\n\t\t\t\t\t\t\tNotification.TABLE_NAME,\n\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\tNotification.whereIdIs(selection),\n\t\t\t\t\t\t\tNotification.whereIdArg(\n\t\t\t\t\t\t\t\t\tLong.parseLong(uri.getLastPathSegment()), selectionArgs));\n\t\t\t\t\tbreak;\n\t\t\t\tcase Notification.BASEURICODE:\n\t\t\t\t\tupdateUris.add(Notification.URI);\n\t\t\t\t\tupdateUris.add(Notification.URI_WITH_TASK_PATH);\n\t\t\t\t\t// No checks\n\t\t\t\t\tresult += db.update(Notification.TABLE_NAME, values, selection, selectionArgs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RemoteTaskList.BASEITEMCODE:\n\t\t\t\t\tupdateUris.add(RemoteTaskList.URI);\n\t\t\t\t\tresult += db.update(RemoteTaskList.TABLE_NAME, values,\n\t\t\t\t\t\t\tRemoteTaskList.whereIdIs(selection),\n\t\t\t\t\t\t\tRemoteTaskList.whereIdArg(\n\t\t\t\t\t\t\t\t\tLong.parseLong(uri.getLastPathSegment()), selectionArgs)\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RemoteTask.BASEITEMCODE:\n\t\t\t\t\tupdateUris.add(RemoteTask.URI);\n\t\t\t\t\tresult += db.update(\n\t\t\t\t\t\t\tRemoteTask.TABLE_NAME,\n\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\tRemoteTask.whereIdIs(selection),\n\t\t\t\t\t\t\tRemoteTask.whereIdArg(\n\t\t\t\t\t\t\t\t\tLong.parseLong(uri.getLastPathSegment()), selectionArgs)\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new IllegalArgumentException(\"Faulty URI provided: \" + uri);\n\t\t\t}\n\n\t\t\tif (result >= 0) {\n\t\t\t\tdb.setTransactionSuccessful();\n\t\t\t}\n\t\t} finally {\n\t\t\tdb.endTransaction();\n\t\t}\n\n\t\tif (result >= 0) {\n\t\t\tfor (Uri u : updateUris) {\n\t\t\t\tDAO.notifyProviderOnChange(getContext(), u);\n\t\t\t}\n\t\t\tUpdateNotifier.notifyChangeList(getContext());\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tsynchronized private int safeDeleteItem(final SQLiteDatabase db,\n\t\t\t\t\t\t\t\t\t\t\tfinal String tableName,\n\t\t\t\t\t\t\t\t\t\t\tfinal Uri uri,\n\t\t\t\t\t\t\t\t\t\t\tfinal String selection,\n\t\t\t\t\t\t\t\t\t\t\tfinal String[] selectionArgs) {\n\t\tdb.beginTransaction();\n\t\tint result = 0;\n\t\ttry {\n\t\t\tresult += db.delete(\n\t\t\t\t\ttableName,\n\t\t\t\t\tDAO.whereIdIs(selection),\n\t\t\t\t\tDAO.joinArrays(selectionArgs,\n\t\t\t\t\t\t\tnew String[] { uri.getLastPathSegment() }));\n\t\t\tdb.setTransactionSuccessful();\n\t\t} finally {\n\t\t\tdb.endTransaction();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tsynchronized public int delete(@NonNull Uri uri, String selection,\n\t\t\t\t\t\t\t\t   String[] selectionArgs) {\n\t\tfinal SQLiteDatabase db = DatabaseHandler.getInstance(getContext())\n\t\t\t\t.getWritableDatabase();\n\t\tint result = 0;\n\t\t// Do not add legacy URIs\n\t\tswitch (sURIMatcher.match(uri)) {\n\t\t\tcase TaskList.BASEITEMCODE:\n\t\t\t\tresult += safeDeleteItem(db, TaskList.TABLE_NAME, uri, selection,\n\t\t\t\t\t\tselectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase TaskList.BASEURICODE:\n\t\t\t\tresult += db.delete(TaskList.TABLE_NAME, selection, selectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase Task.BASEITEMCODE:\n\t\t\t\tresult += safeDeleteItem(db, Task.TABLE_NAME, uri, selection,\n\t\t\t\t\t\tselectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase Task.BASEURICODE:\n\t\t\t\tresult += db.delete(Task.TABLE_NAME, selection, selectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase Notification.BASEURICODE:\n\t\t\t\tresult += db.delete(Notification.TABLE_NAME, selection,\n\t\t\t\t\t\tselectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase Notification.BASEITEMCODE:\n\t\t\tcase Notification.WITHTASKQUERYITEMCODE:\n\t\t\t\tresult += safeDeleteItem(db, Notification.TABLE_NAME, uri,\n\t\t\t\t\t\tselection, selectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase RemoteTaskList.BASEURICODE:\n\t\t\t\tresult += db.delete(RemoteTaskList.TABLE_NAME, selection,\n\t\t\t\t\t\tselectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase RemoteTaskList.BASEITEMCODE:\n\t\t\t\tresult += safeDeleteItem(db, RemoteTaskList.TABLE_NAME, uri,\n\t\t\t\t\t\tselection, selectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase RemoteTask.BASEURICODE:\n\t\t\t\tresult += db\n\t\t\t\t\t\t.delete(RemoteTask.TABLE_NAME, selection, selectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase RemoteTask.BASEITEMCODE:\n\t\t\t\tresult += safeDeleteItem(db, RemoteTask.TABLE_NAME, uri, selection,\n\t\t\t\t\t\tselectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase Task.DELETEDQUERYCODE:\n\t\t\t\tresult += db.delete(Task.DELETE_TABLE_NAME, selection,\n\t\t\t\t\t\tselectionArgs);\n\t\t\t\tbreak;\n\t\t\tcase Task.DELETEDITEMCODE:\n\t\t\t\tresult += safeDeleteItem(db, Task.DELETE_TABLE_NAME, uri,\n\t\t\t\t\t\tselection, selectionArgs);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Faulty delete-URI provided: \" + uri);\n\t\t}\n\n\t\tif (result > 0) {\n\t\t\tObjects.requireNonNull(getContext());\n\t\t\tDAO.notifyProviderOnChange(getContext(), uri);\n\t\t\tDAO.notifyProviderOnChange(getContext(), TaskList.URI_WITH_COUNT);\n\t\t\tUpdateNotifier.updateWidgets(getContext());\n\t\t\tUpdateNotifier.notifyChangeList(getContext());\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tsynchronized public Cursor query(@NonNull Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t String[] selectionArgs, String sortOrder) {\n\t\tCursor result;\n\t\tfinal long id;\n\t\tObjects.requireNonNull(getContext());\n\t\tswitch (sURIMatcher.match(uri)) {\n\t\t\tcase TaskList.BASEURICODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(TaskList.TABLE_NAME, projection, selection,\n\t\t\t\t\t\t\t\tselectionArgs, null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), TaskList.URI);\n\t\t\t\tbreak;\n\t\t\tcase TaskList.BASEITEMCODE:\n\t\t\t\tid = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(TaskList.TABLE_NAME,\n\t\t\t\t\t\t\t\tprojection,\n\t\t\t\t\t\t\t\tTaskList.whereIdIs(selection),\n\t\t\t\t\t\t\t\tTaskList.joinArrays(selectionArgs,\n\t\t\t\t\t\t\t\t\t\tnew String[] { String.valueOf(id) }),\n\t\t\t\t\t\t\t\tnull, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase TaskList.VIEWCOUNTCODE:\n\t\t\t\t// Create view if not exists\n\t\t\t\tDatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getWritableDatabase()\n\t\t\t\t\t\t.execSQL(TaskList.CREATE_COUNT_VIEW);\n\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t// may crash here. android.database.sqlite.SQLiteException: no such table: lists_with_count (code 1 SQLITE_ERROR): , while compiling: SELECT _id, title, count FROM lists_with_count ORDER BY title COLLATE NOCASE\n\t\t\t\t\t\t.query(TaskList.VIEWCOUNT_NAME, projection, selection,\n\t\t\t\t\t\t\t\tselectionArgs, null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase Task.DELETEDQUERYCODE:\n\t\t\t\tfinal String[] query = sanitize(selectionArgs);\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.DELETE_TABLE_NAME,\n\t\t\t\t\t\t\t\tTask.Columns.DELETEFIELDS,\n\t\t\t\t\t\t\t\tTask.Columns._ID + \" IN (SELECT \" + Task.Columns._ID\n\t\t\t\t\t\t\t\t\t\t+ \" FROM \" + Task.FTS3_DELETE_TABLE_NAME\n\t\t\t\t\t\t\t\t\t\t+ ((query[0].isEmpty() || query[0].equals(\"'*'\")) ? \")\"\n\t\t\t\t\t\t\t\t\t\t: (\" WHERE \" + Task.FTS3_DELETE_TABLE_NAME + \" MATCH ?)\")),\n\t\t\t\t\t\t\t\t(query[0].isEmpty() || query[0].equals(\"'*'\")) ? null : query,\n\t\t\t\t\t\t\t\tnull, null, sortOrder);\n\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), Task.URI_DELETED_QUERY);\n\t\t\t\tbreak;\n\t\t\tcase Task.BASEURICODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.TABLE_NAME, projection, selection,\n\t\t\t\t\t\t\t\tselectionArgs, null, null, sortOrder);\n\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(),\n\t\t\t\t\t\tTask.URI);\n\t\t\t\tbreak;\n\t\t\tcase Task.BASEITEMCODE:\n\t\t\t\tid = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.TABLE_NAME,\n\t\t\t\t\t\t\t\tprojection,\n\t\t\t\t\t\t\t\tTask.whereIdIs(selection),\n\t\t\t\t\t\t\t\tTask.joinArrays(selectionArgs, new String[] { String.valueOf(id) }),\n\t\t\t\t\t\t\t\tnull, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase Task.SECTIONEDDATEQUERYCODE:\n\t\t\t\t// this branch runs when the user sorts notes by due date\n\n\t\t\t\t// Add list null because that's what the headers will have\n\t\t\t\tfinal String listId;\n\t\t\t\tif (selectionArgs == null || selectionArgs.length == 0) {\n\t\t\t\t\tlistId = null;\n\t\t\t\t\t// throw new SQLException(\"Need a listid as first arg at the moment for this view!\");\n\t\t\t\t} else {\n\t\t\t\t\tlistId = selectionArgs[0];\n\t\t\t\t}\n\n\t\t\t\t// Create view if not exists\n\t\t\t\t// TODO as explained in issue #525, on older OS versions (API 34 emulator, ...)\n\t\t\t\t//  this function returns a query to make a view with a column \"dblist\" of type\n\t\t\t\t//  INTEGER, as expected. On the Google Pixel 8a with android 14, and on API 35\n\t\t\t\t//  emulators, the column \"dblist\" is of type BLOB, which is not correct\n\t\t\t\tString NNN_DATE_VIEW_QUERY = Task.CREATE_SECTIONED_DATE_VIEW(listId);\n\t\t\t\tDatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getWritableDatabase()\n\t\t\t\t\t\t.execSQL(NNN_DATE_VIEW_QUERY);\n\n\t\t\t\t// this cursor contains the real notes (read from the database)\n\t\t\t\t// and some \"artificial\" records used as headers for the groups of notes\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.getSECTION_DATE_VIEW_NAME(listId),\n\t\t\t\t\t\t\t\tprojection,\n\t\t\t\t\t\t\t\tselection,\n\t\t\t\t\t\t\t\tselectionArgs,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tTask.SECRET_TYPEID + \",\" + Task.Columns.DUE + \",\"\n\t\t\t\t\t\t\t\t\t\t+ Task.SECRET_TYPEID2);\n\n\t\t\t\t// You can see that the cursor contains both fake records like \"today+1\"\n\t\t\t\t// and real notes taken from the database\n\t\t\t\t// String DBG_READABLE_CURSOR_DUMP = DatabaseUtils.dumpCursorToString(result);\n\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(),\n\t\t\t\t\t\tTask.URI);\n\t\t\t\tbreak;\n\t\t\tcase Task.HISTORYQUERYCODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.HISTORY_TABLE_NAME, projection, selection,\n\t\t\t\t\t\t\t\tselectionArgs, null, null,\n\t\t\t\t\t\t\t\tTask.Columns.UPDATED + \" ASC\");\n\t\t\t\t// SQLite timestamp in updated column.\n\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase Notification.BASEITEMCODE:\n\t\t\t\tid = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Notification.TABLE_NAME,\n\t\t\t\t\t\t\t\tprojection,\n\t\t\t\t\t\t\t\tNotification.whereIdIs(selection),\n\t\t\t\t\t\t\t\tNotification.joinArrays(selectionArgs,\n\t\t\t\t\t\t\t\t\t\tnew String[] { String.valueOf(id) }), null,\n\t\t\t\t\t\t\t\tnull, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase Notification.WITHTASKQUERYITEMCODE:\n\t\t\t\t// Create view if not exists\n\t\t\t\tDatabaseHandler.getInstance(getContext()).getWritableDatabase()\n\t\t\t\t\t\t.execSQL(Notification.CREATE_JOINED_VIEW);\n\t\t\t\tid = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Notification.WITH_TASK_VIEW_NAME,\n\t\t\t\t\t\t\t\tprojection,\n\t\t\t\t\t\t\t\tNotification.whereIdIs(selection),\n\t\t\t\t\t\t\t\tNotification.joinArrays(selectionArgs,\n\t\t\t\t\t\t\t\t\t\tnew String[] { String.valueOf(id) }), null,\n\t\t\t\t\t\t\t\tnull, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase Notification.BASEURICODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Notification.TABLE_NAME, projection, selection,\n\t\t\t\t\t\t\t\tselectionArgs, null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase Notification.WITHTASKQUERYCODE:\n\t\t\t\t// Create view if not exists\n\t\t\t\tDatabaseHandler.getInstance(getContext()).getWritableDatabase()\n\t\t\t\t\t\t.execSQL(Notification.CREATE_JOINED_VIEW);\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Notification.WITH_TASK_VIEW_NAME, projection,\n\t\t\t\t\t\t\t\tselection, selectionArgs, null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase RemoteTaskList.BASEURICODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(RemoteTaskList.TABLE_NAME, projection, selection,\n\t\t\t\t\t\t\t\tselectionArgs, null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase RemoteTask.BASEURICODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(RemoteTask.TABLE_NAME, projection, selection,\n\t\t\t\t\t\t\t\tselectionArgs, null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t\t\tbreak;\n\t\t\tcase Task.SEARCHCODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.TABLE_NAME,\n\t\t\t\t\t\t\t\tTask.Columns.FIELDS,\n\t\t\t\t\t\t\t\tTask.Columns._ID + \" IN (SELECT \"\n\t\t\t\t\t\t\t\t\t\t+ Task.Columns._ID + \" FROM \"\n\t\t\t\t\t\t\t\t\t\t+ Task.FTS3_TABLE_NAME + \" WHERE \"\n\t\t\t\t\t\t\t\t\t\t+ Task.FTS3_TABLE_NAME + \" MATCH ?)\",\n\t\t\t\t\t\t\t\tsanitize(selectionArgs), null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(),\n\t\t\t\t\t\tTask.URI_SEARCH);\n\t\t\t\tbreak;\n\n\t\t\tcase TaskList.LEGACYBASEURICODE:\n\t\t\tcase TaskList.LEGACYVISIBLEURICODE:\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(TaskList.TABLE_NAME,\n\t\t\t\t\t\t\t\tLegacyDBHelper.convertLegacyColumns(projection),\n\t\t\t\t\t\t\t\tnull, null, null, null, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(),\n\t\t\t\t\t\tTaskList.URI);\n\t\t\t\tbreak;\n\t\t\tcase TaskList.LEGACYBASEITEMCODE:\n\t\t\tcase TaskList.LEGACYVISIBLEITEMCODE:\n\t\t\t\tid = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(TaskList.TABLE_NAME,\n\t\t\t\t\t\t\t\tLegacyDBHelper.convertLegacyColumns(projection),\n\t\t\t\t\t\t\t\tTaskList.whereIdIs(selection),\n\t\t\t\t\t\t\t\tTaskList.joinArrays(selectionArgs,\n\t\t\t\t\t\t\t\t\t\tnew String[] { String.valueOf(id) }), null,\n\t\t\t\t\t\t\t\tnull, sortOrder);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(),\n\t\t\t\t\t\tTaskList.getUri(id));\n\t\t\t\tbreak;\n\t\t\tcase Task.LEGACYBASEURICODE:\n\t\t\tcase Task.LEGACYVISIBLEURICODE:\n\t\t\t\tfinal Cursor c = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.TABLE_NAME,\n\t\t\t\t\t\t\t\tLegacyDBHelper.convertLegacyColumns(projection),\n\t\t\t\t\t\t\t\tnull, null, null, null, Task.Columns.DUE);\n\t\t\t\tresult = new MatrixCursor(projection);\n\t\t\t\twhile (c.moveToNext()) {\n\t\t\t\t\t((MatrixCursor) result).addRow(LegacyDBHelper\n\t\t\t\t\t\t\t.convertLegacyTaskValues(c));\n\t\t\t\t}\n\t\t\t\tc.close();\n\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(),\n\t\t\t\t\t\tTask.URI);\n\t\t\t\tbreak;\n\t\t\tcase Task.SEARCHSUGGESTIONSCODE:\n\t\t\t\tfinal String limit = uri\n\t\t\t\t\t\t.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT);\n\t\t\t\tresult = DatabaseHandler\n\t\t\t\t\t\t.getInstance(getContext())\n\t\t\t\t\t\t.getReadableDatabase()\n\t\t\t\t\t\t.query(Task.FTS3_TABLE_NAME,\n\t\t\t\t\t\t\t\tnew String[] {\n\t\t\t\t\t\t\t\t\t\tTask.Columns._ID,\n\t\t\t\t\t\t\t\t\t\tTask.Columns._ID\n\t\t\t\t\t\t\t\t\t\t\t\t+ \" AS \"\n\t\t\t\t\t\t\t\t\t\t\t\t+ SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID,\n\t\t\t\t\t\t\t\t\t\tTask.Columns.TITLE\n\t\t\t\t\t\t\t\t\t\t\t\t+ \" AS \"\n\t\t\t\t\t\t\t\t\t\t\t\t+ SearchManager.SUGGEST_COLUMN_TEXT_1,\n\t\t\t\t\t\t\t\t\t\tTask.Columns.NOTE\n\t\t\t\t\t\t\t\t\t\t\t\t+ \" AS \"\n\t\t\t\t\t\t\t\t\t\t\t\t+ SearchManager.SUGGEST_COLUMN_TEXT_2 },\n\t\t\t\t\t\t\t\tTask.FTS3_TABLE_NAME + \" MATCH ?\",\n\t\t\t\t\t\t\t\tsanitize(selectionArgs), null, null,\n\t\t\t\t\t\t\t\tSearchManager.SUGGEST_COLUMN_TEXT_1, limit);\n\t\t\t\tresult.setNotificationUri(getContext().getContentResolver(),\n\t\t\t\t\t\tTask.URI_SEARCH);\n\t\t\t\tbreak;\n\t\t\t// These legacy URIs will not be supported\n\t\t\tcase Task.LEGACYBASEITEMCODE:\n\t\t\tcase Task.LEGACYVISIBLEITEMCODE:\n\t\t\tdefault:\n\t\t\t\tNnnLogger.debug(MyContentProvider.class, \"Faulty queryURI provided: \" + uri);\n\t\t\t\treturn null;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate String[] sanitize(final String... args) {\n\t\tif (args.length == 0) return new String[] { \"\" };\n\n\t\tfinal StringBuilder result = new StringBuilder();\n\t\tfor (String query : args) {\n\t\t\t// for (String part : query.split(\"\\\\s\")) {\n\t\t\tif (result.length() > 0) result.append(\" AND \");\n\t\t\t// Wrap each word in quotes and add star to the end\n\t\t\tresult.append(\"'\").append(query).append(\"*'\");\n\t\t\t// }\n\t\t}\n\n\t\treturn new String[] { result.toString() };\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/Notification.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\nimport android.view.View;\n\nimport androidx.annotation.Nullable;\n\nimport com.nononsenseapps.helpers.NotificationHelper;\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.ui.WeekDaysView;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.List;\nimport java.util.concurrent.Executors;\n\n/**\n * A model for the SQLite table where reminders are saved\n */\npublic class Notification extends DAO {\n\n\t// TODO see if you can rename this to \"Reminder\" without ruining the database logic\n\n\t// These match WeekDaysView's values\n\tpublic static final int mon = 0x1;\n\tpublic static final int tue = 0x10;\n\tpublic static final int wed = 0x100;\n\tpublic static final int thu = 0x1000;\n\tpublic static final int fri = 0x10000;\n\tpublic static final int sat = 0x100000;\n\tpublic static final int sun = 0x1000000;\n\t// TODO maybe with more flags we can add special types of repeat,\n\t//  but we have to use long instead of int:\n\t//  public static final long nextMonth = 0x100000000;\n\n\t// TODO see if you can move these flags to an enum\n\n\t// Location repeat, one left of sun\n\tpublic static final int locationRepeat = 0x10000000;\n\n\t// SQL convention says Table name should be \"singular\"\n\tpublic static final String TABLE_NAME = \"notification\";\n\tpublic static final String WITH_TASK_VIEW_NAME = \"notification_with_tasks\";\n\tpublic static final String WITH_TASK_PATH = TABLE_NAME + \"/with_task_info\";\n\n\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.\"\n\t\t\t+ TABLE_NAME;\n\n\tpublic static final Uri URI = Uri.withAppendedPath(\n\t\t\tUri.parse(MyContentProvider.SCHEME + MyContentProvider.AUTHORITY), TABLE_NAME);\n\tpublic static final Uri URI_WITH_TASK_PATH = Uri.withAppendedPath(\n\t\t\tUri.parse(MyContentProvider.SCHEME + MyContentProvider.AUTHORITY), WITH_TASK_PATH);\n\n\tpublic static final int BASEURICODE = 301;\n\tpublic static final int BASEITEMCODE = 302;\n\tpublic static final int WITHTASKQUERYCODE = 303;\n\tpublic static final int WITHTASKQUERYITEMCODE = 304;\n\n\tpublic static void addMatcherUris(UriMatcher sURIMatcher) {\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME, BASEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME + \"/#\", BASEITEMCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, WITH_TASK_PATH, WITHTASKQUERYCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, WITH_TASK_PATH + \"/#\",\n\t\t\t\tWITHTASKQUERYITEMCODE);\n\t}\n\n\tpublic static Uri getUri(final long id) {\n\t\treturn Uri.withAppendedPath(URI, Long.toString(id));\n\t}\n\n\tpublic static class Columns implements BaseColumns {\n\n\t\tprivate Columns() {}\n\n\t\tpublic static final String TIME = \"time\";\n\t\tpublic static final String PERMANENT = \"permanent\";\n\t\tpublic static final String TASKID = \"taskid\";\n\t\tpublic static final String REPEATS = \"repeats\";\n\t\tpublic static final String LATITUDE = \"latitude\";\n\t\tpublic static final String LONGITUDE = \"longitude\";\n\t\tpublic static final String RADIUS = \"radius\";\n\t\tpublic static final String LOCATIONNAME = \"locationname\";\n\n\t\tpublic static final String[] FIELDS = { _ID, TIME, PERMANENT, TASKID, REPEATS,\n\t\t\t\tLOCATIONNAME, LATITUDE, LONGITUDE, RADIUS };\n\t}\n\n\tpublic static class ColumnsWithTask extends Columns {\n\n\t\tprivate ColumnsWithTask() {}\n\n\t\t// public static final String notificationPrefix = \"n.\";\n\t\tpublic static final String taskPrefix = \"t_\";\n\t\tpublic static final String listPrefix = \"l_\";\n\n\t\tpublic static final String[] FIELDS = joinArrays(\n\t\t\t\t// prefixArray(notificationPrefix, Columns.FIELDS),\n\t\t\t\tColumns.FIELDS,\n\t\t\t\tprefixArray(taskPrefix, Task.Columns.SHALLOWFIELDS),\n\t\t\t\tprefixArray(listPrefix, TaskList.Columns.SHALLOWFIELDS));\n\t}\n\n\t/**\n\t * Main table to store notification data\n\t */\n\tpublic static final String CREATE_TABLE = \"CREATE TABLE \" +\n\t\t\tTABLE_NAME +\n\t\t\t\"(\" +\n\t\t\tColumns._ID +\n\t\t\t\" INTEGER PRIMARY KEY,\" +\n\t\t\tColumns.TIME +\n\t\t\t\" INTEGER,\" +\n\t\t\tColumns.PERMANENT +\n\t\t\t\" INTEGER NOT NULL DEFAULT 0,\" +\n\t\t\tColumns.TASKID +\n\t\t\t\" INTEGER,\" +\n\t\t\t// Interpreted binary\n\t\t\tColumns.REPEATS +\n\t\t\t\" INTEGER NOT NULL DEFAULT 0,\" +\n\t\t\t// Location data\n\t\t\tColumns.LOCATIONNAME + \" TEXT,\" +\n\t\t\tColumns.LATITUDE + \" REAL, \" +\n\t\t\tColumns.LONGITUDE +\n\t\t\t\" REAL, \" +\n\t\t\tColumns.RADIUS +\n\t\t\t\" REAL, \" +\n\t\t\t// Foreign key for task\n\t\t\t\"FOREIGN KEY(\" + Columns.TASKID +\n\t\t\t\") REFERENCES \" + Task.TABLE_NAME + \"(\" +\n\t\t\tTask.Columns._ID + \") ON DELETE CASCADE\" +\n\t\t\t\")\";\n\n\t/**\n\t * View that joins relevant data from tasks and lists tables\n\t */\n\tpublic static final String CREATE_JOINED_VIEW = \"CREATE TEMP VIEW IF NOT EXISTS \" +\n\t\t\tWITH_TASK_VIEW_NAME + \" AS \" + \" SELECT \" +\n\t\t\t// Notifications as normal column names\n\t\t\tarrayToCommaString(TABLE_NAME + \".\", Columns.FIELDS) + \",\" +\n\t\t\t// Rest gets prefixed\n\t\t\tarrayToCommaString(\"t.\", Task.Columns.SHALLOWFIELDS, \" AS \"\n\t\t\t\t\t+ ColumnsWithTask.taskPrefix + \"%1$s\") + \",\" +\n\t\t\tarrayToCommaString(\"l.\", TaskList.Columns.SHALLOWFIELDS, \" AS \"\n\t\t\t\t\t+ ColumnsWithTask.listPrefix + \"%1$s\") +\n\t\t\t\" FROM \" + TABLE_NAME + \",\" + Task.TABLE_NAME + \" AS t,\" + TaskList.TABLE_NAME\n\t\t\t+ \" AS l \" + \" WHERE \" + TABLE_NAME + \".\" + Columns.TASKID + \" = t.\"\n\t\t\t+ Task.Columns._ID + \" AND t.\" + Task.Columns.DBLIST + \" = l.\" +\n\t\t\tTaskList.Columns._ID + \";\";\n\n\t/**\n\t * A {@link Task} can have reminders, which are {@link Notification} objects.\n\t * They are used to show (android) notifications at a user-provided day and time.\n\t * Here it is expressed in milliseconds since 1970-01-01 UTC.\n\t */\n\tpublic Long time = null;\n\n\t/**\n\t * It's initialized, but never used. I think I'll use it for sticky reminder notifications!\n\t */\n\tpublic boolean permanent = false;\n\n\t/**\n\t * The {@link Task#_id} of the {@link Task} that this reminder is for\n\t */\n\tpublic Long taskID = null;\n\n\t/**\n\t * flags to indicate on which week days the note repeats. See {@link #sat} for example\n\t */\n\tpublic long repeats = 0;\n\t// TODO make \"repeats\" private, and use .isRepeating() instead\n\n\tpublic String locationName = null;\n\tpublic Double latitude = null;\n\tpublic Double longitude = null;\n\tpublic Double radius = null;\n\n\t// Read only, fetched from VIEW\n\tpublic String listTitle = null;\n\tpublic Long listID = null;\n\tpublic String taskTitle = null;\n\tpublic String taskNote = null;\n\n\t// Convenience for the editor\n\tpublic View view = null;\n\n\t/**\n\t * Must be associated with a task\n\t */\n\tpublic Notification(final long taskID) {\n\t\tthis.taskID = taskID;\n\t}\n\n\tpublic Notification(final Cursor c) {\n\t\t_id = c.getLong(0);\n\t\ttime = c.isNull(1) ? null : c.getLong(1);\n\t\tpermanent = 1 == c.getLong(2);\n\t\ttaskID = c.isNull(3) ? null : c.getLong(3);\n\t\trepeats = c.getLong(4);\n\t\tlocationName = c.isNull(5) ? null : c.getString(5);\n\t\tlatitude = c.isNull(6) ? null : c.getDouble(6);\n\t\tlongitude = c.isNull(7) ? null : c.getDouble(7);\n\t\tradius = c.isNull(8) ? null : c.getDouble(8);\n\t\t// if cursor has more fields, then assume it was constructed with\n\t\t// the WITH_TASKS view query\n\t\tif (c.getColumnCount() > 9) {\n\t\t\tint idx_list = c.getColumnIndex(ColumnsWithTask.listPrefix + TaskList.Columns.TITLE);\n\t\t\tint idx_id = c.getColumnIndex(ColumnsWithTask.listPrefix + TaskList.Columns._ID);\n\t\t\tint idx_title = c.getColumnIndex(ColumnsWithTask.taskPrefix + Task.Columns.TITLE);\n\t\t\tint idx_note = c.getColumnIndex(ColumnsWithTask.taskPrefix + Task.Columns.NOTE);\n\t\t\tlistTitle = c.getString(idx_list);\n\t\t\tlistID = c.getLong(idx_id);\n\t\t\ttaskTitle = c.getString(idx_title);\n\t\t\ttaskNote = c.getString(idx_note);\n\t\t}\n\t}\n\n\tpublic Notification(final Uri uri, final ContentValues values) {\n\t\tthis(Long.parseLong(uri.getLastPathSegment()), values);\n\t}\n\n\tpublic Notification(final long id, final ContentValues values) {\n\t\tthis(values);\n\t\t_id = id;\n\t}\n\n\t/**\n\t * @param uri like content://com.nononsenseapps.NotePad/notification/1\n\t * @return the {@link Notification} with the {@link #_id} in the given {@link Uri}, or NULL\n\t * if it didn't find (exactly) one\n\t */\n\t@Nullable\n\tpublic static Notification fromUri(final Uri uri, final Context context) {\n\t\tfinal Cursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(uri, Columns.FIELDS, null, null, null);\n\t\tassert c != null;\n\t\tNotification n = null;\n\t\tif (c.getCount() == 1) {\n\t\t\tc.moveToFirst();\n\t\t\tn = new Notification(c);\n\t\t}\n\t\tc.close();\n\t\treturn n;\n\t}\n\n\tpublic Notification(final JSONObject json) throws JSONException {\n\t\tif (json.has(Columns.TIME))\n\t\t\ttime = json.getLong(Columns.TIME);\n\t\tif (json.has(Columns.PERMANENT))\n\t\t\tpermanent = 1 == json.getLong(Columns.PERMANENT);\n\t\tif (json.has(Columns.TASKID))\n\t\t\ttaskID = json.getLong(Columns.TASKID);\n\t\tif (json.has(Columns.REPEATS))\n\t\t\trepeats = json.getLong(Columns.REPEATS);\n\t\tif (json.has(Columns.LOCATIONNAME))\n\t\t\tlocationName = json.getString(Columns.LOCATIONNAME);\n\t\tif (json.has(Columns.LATITUDE))\n\t\t\tlatitude = json.getDouble(Columns.LATITUDE);\n\t\tif (json.has(Columns.LONGITUDE))\n\t\t\tlongitude = json.getDouble(Columns.LONGITUDE);\n\t\tif (json.has(Columns.RADIUS))\n\t\t\tradius = json.getDouble(Columns.RADIUS);\n\t}\n\n\tpublic Notification(final ContentValues values) {\n\t\ttime = values.getAsLong(Columns.TIME);\n\t\tpermanent = 1 == values.getAsLong(Columns.PERMANENT);\n\t\ttaskID = values.getAsLong(Columns.TASKID);\n\t\trepeats = values.getAsLong(Columns.REPEATS);\n\t\tlocationName = values.getAsString(Columns.LOCATIONNAME);\n\t\tlatitude = values.getAsDouble(Columns.LATITUDE);\n\t\tlongitude = values.getAsDouble(Columns.LONGITUDE);\n\t\tradius = values.getAsDouble(Columns.RADIUS);\n\t}\n\n\t@Override\n\tpublic ContentValues getContent() {\n\t\tfinal ContentValues values = new ContentValues();\n\n\t\tvalues.put(Columns.TIME, time);\n\t\tvalues.put(Columns.TASKID, taskID);\n\t\tvalues.put(Columns.PERMANENT, permanent ? 1 : 0);\n\t\tvalues.put(Columns.REPEATS, repeats);\n\t\tvalues.put(Columns.LOCATIONNAME, locationName);\n\t\tvalues.put(Columns.LATITUDE, latitude);\n\t\tvalues.put(Columns.LONGITUDE, longitude);\n\t\tvalues.put(Columns.RADIUS, radius);\n\n\t\treturn values;\n\t}\n\n\t@Override\n\tprotected String getTableName() {\n\t\treturn TABLE_NAME;\n\t}\n\n\t@Override\n\tpublic String getContentType() {\n\t\treturn CONTENT_TYPE;\n\t}\n\n\t/**\n\t * Returns date and time formatted in text in local time zone\n\t */\n\tpublic CharSequence getLocalDateTimeText(final Context context) {\n\t\treturn TimeFormatter.getLocalDateStringLong(context, time);\n\t}\n\n\t/**\n\t * Returns time formatted in text in local time zone\n\t */\n\tpublic CharSequence getLocalTimeText(final Context context) {\n\t\treturn TimeFormatter.getLocalTimeOnlyString(context, time);\n\t}\n\n\t/**\n\t * Returns date formatted in text in local time zone\n\t */\n\tpublic CharSequence getLocalDateText(final Context context) {\n\t\treturn TimeFormatter.getDateFormatter(context).format(new Date(time));\n\t}\n\n\t/**\n\t * Calls {@link #insert} or performs an update, depending on the status of this\n\t * {@link Notification} object\n\t */\n\t@Override\n\tpublic int save(final Context context) {\n\t\tint result = 0;\n\t\tif (_id < 1) {\n\t\t\tresult += insert(context);\n\t\t} else {\n\t\t\tresult += context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.update(getUri(), getContent(), null, null);\n\t\t\tif (result < 1) {\n\t\t\t\t// To allow editor to edit deleted notifications\n\t\t\t\tresult += insert(context);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Inserts this {@link Notification} as a new record into its SQLite table\n\t * and sets its {@link #_id}\n\t *\n\t * @return 1 if it worked, 0 if it failed\n\t */\n\tprivate int insert(final Context context) {\n\t\tint result = 0;\n\t\tfinal Uri uri = context.getContentResolver().insert(getBaseUri(), getContent());\n\t\tif (uri != null) {\n\t\t\t_id = Long.parseLong(uri.getLastPathSegment());\n\t\t\tresult++;\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * If true, will also schedule/notify android notifications\n\t */\n\tpublic void save(final Context context, final boolean schedule) {\n\t\tint result = save(context);\n\t\tif (schedule) {\n\t\t\t// First cancel any potentially old versions\n\t\t\tNotificationHelper.cancelNotification(context, this);\n\t\t\t// Then reschedule\n\t\t\tNotificationHelper.schedule(context);\n\t\t}\n\t}\n\n\t/**\n\t * Delete the record of this {@link Notification} from the database and remove the\n\t * associated {@link android.app.Notification} from the system's tray\n\t *\n\t * @return 1 if it was deleted, 0 otherwise\n\t */\n\t@Override\n\tpublic int delete(final Context context) {\n\t\t// Make sure existing notifications are cancelled.\n\t\tNotificationHelper.cancelNotification(context, this);\n\t\treturn super.delete(context);\n\t}\n\n\tpublic void saveInBackground(final Context context, final boolean schedule) {\n\t\tExecutors.newSingleThreadExecutor().execute(() -> save(context, schedule));\n\t}\n\n\t/**\n\t * Starts a background task that removes all notifications associated with\n\t * the specified tasks.\n\t */\n\tpublic static void removeWithTaskIds(final Context context, final Long... ids) {\n\t\tif (ids.length > 0) {\n\t\t\t// replacement for AsyncTask<,,>\n\t\t\tExecutors.newSingleThreadExecutor().execute(() -> {\n\t\t\t\t// Background work here\n\t\t\t\tremoveWithTaskIdsSynced(context, ids);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Removes all notifications associated with the specified tasks. Runs in\n\t * the same thread as the caller.\n\t */\n\tpublic static void removeWithTaskIdsSynced(final Context context, final Long... ids) {\n\t\tString idStrings = \"(\";\n\t\tArrayList<String> idsToClear = new ArrayList<>();\n\t\tfor (Long id : ids) {\n\t\t\tidStrings += id + \",\";\n\t\t\tidsToClear.add(Long.toString(id));\n\t\t}\n\t\tidStrings = idStrings.substring(0, idStrings.length() - 1);\n\t\tidStrings += \")\";\n\n\t\tfinal Cursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(URI, Columns.FIELDS, Columns.TASKID + \" IN \" + idStrings,\n\t\t\t\t\t\tnull, null);\n\t\tassert c != null;\n\t\twhile (c.moveToNext()) {\n\t\t\t// Yes dont just call delete in database\n\t\t\t// We have to remove geofences (in delete)\n\t\t\tNotification n = new Notification(c);\n\t\t\tn.delete(context);\n\t\t}\n\t\tc.close();\n\t}\n\n\t/**\n\t * Delete or reschedule a specific notification.\n\t *\n\t * @param uri like content://com.nononsenseapps.NotePad/notification/1\n\t */\n\tpublic static void deleteOrReschedule(final Context context, final Uri uri) {\n\t\tfinal Cursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(uri, Columns.FIELDS, null, null, null);\n\t\tassert c != null;\n\t\twhile (c.moveToNext()) {\n\t\t\tNotification n = new Notification(c);\n\t\t\tn.deleteOrReschedule(context);\n\t\t}\n\t\tc.close();\n\t}\n\n\t/**\n\t * Returns list of notifications coupled to specified task, sorted by time\n\t */\n\tpublic static List<Notification> getNotificationsOfTask(final Context context, final long taskId) {\n\t\treturn getNotificationsWithTasks(context, Columns.TASKID + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(taskId) }, Columns.TIME);\n\t}\n\n\t/**\n\t * @return a list of notifications occurring after/before specified time,\n\t * and which do not have a location (radius == null). Sorted by time\n\t * ascending\n\t */\n\tpublic static List<Notification> getNotificationsWithTime(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  final long time,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  final boolean before) {\n\t\tfinal String comparison = before ? \" <= ?\" : \" > ?\";\n\t\treturn getNotificationsWithTasks(context,\n\t\t\t\tColumns.TIME + comparison + \" AND \" + Columns.RADIUS + \" IS NULL\",\n\t\t\t\tnew String[] { Long.toString(time) }, Columns.TIME);\n\t}\n\n\tpublic static List<Notification> getNotificationsWithTasks(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   final String where,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   final String[] whereArgs,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   final String sortOrder) {\n\t\tArrayList<Notification> list = new ArrayList<>();\n\t\tfinal Cursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(URI_WITH_TASK_PATH, null, where, whereArgs, sortOrder);\n\t\tif (c != null) {\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tlist.add(new Notification(c));\n\t\t\t}\n\t\t\tc.close();\n\t\t}\n\t\treturn list;\n\t}\n\n\t/**\n\t * Used for snooze, only for non-repeating reminders\n\t *\n\t * @param newTime from {@link NotificationHelper#getSnoozedReminderNewTimeMillis}\n\t */\n\tpublic static void setTime(final Context context, final Uri uri, final long newTime) {\n\t\tfinal ContentValues values = new ContentValues();\n\t\tvalues.put(Columns.TIME, newTime);\n\t\t// Use base ID to bypass type checks\n\t\tcontext.getContentResolver()\n\t\t\t\t.update(URI, values, Columns._ID + \" IS ?\",\n\t\t\t\t\t\tnew String[] { uri.getLastPathSegment() });\n\t}\n\n\t/**\n\t * Used for snooze\n\t */\n\tpublic static void setTimeForListAndBefore(final Context context, final long listId,\n\t\t\t\t\t\t\t\t\t\t\t   final long maxTime, final long newTime) {\n\t\t// replacement for AsyncTask<,,>\n\t\tExecutors.newSingleThreadExecutor().execute(() -> {\n\t\t\t// Background work here\n\t\t\t// First get the list of tasks in that list\n\t\t\tfinal Cursor c = context.getContentResolver()\n\t\t\t\t\t.query(Task.URI, Task.Columns.FIELDS, Task.Columns.DBLIST\n\t\t\t\t\t\t\t\t\t+ \" IS ? AND \"\n\t\t\t\t\t\t\t\t\t+ com.nononsenseapps.notepad.database.Notification.Columns.RADIUS\n\t\t\t\t\t\t\t\t\t+ \" IS NULL\", new String[] { Long.toString(listId) },\n\t\t\t\t\t\t\tnull);\n\t\t\tassert c != null;\n\t\t\tString idStrings = \"(\";\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tidStrings += c.getLong(0) + \",\";\n\t\t\t}\n\t\t\tc.close();\n\t\t\tidStrings = idStrings.substring(0, idStrings.length() - 1);\n\t\t\tidStrings += \")\";\n\n\t\t\tfinal ContentValues values = new ContentValues();\n\t\t\tvalues.put(Columns.TIME, newTime);\n\n\t\t\tcontext.getContentResolver().update(URI, values,\n\t\t\t\t\tColumns.TIME + \" <= \" + maxTime + \" AND \" + Columns.TASKID + \" IN \"\n\t\t\t\t\t\t\t+ idStrings, null);\n\t\t});\n\t}\n\n\t/**\n\t * Returns true if the notification repeats on the given day. Day of the\n\t * week as given by Calendar.getField(DayOfWeek)\n\t */\n\tpublic boolean repeatsOn(final int calendarDay) {\n\t\tint day = switch (calendarDay) {\n\t\t\tcase Calendar.MONDAY -> WeekDaysView.mon;\n\t\t\tcase Calendar.TUESDAY -> WeekDaysView.tue;\n\t\t\tcase Calendar.WEDNESDAY -> WeekDaysView.wed;\n\t\t\tcase Calendar.THURSDAY -> WeekDaysView.thu;\n\t\t\tcase Calendar.FRIDAY -> WeekDaysView.fri;\n\t\t\tcase Calendar.SATURDAY -> WeekDaysView.sat;\n\t\t\tcase Calendar.SUNDAY -> WeekDaysView.sun;\n\t\t\tdefault -> 0;\n\t\t};\n\n\t\treturn (0 < (day & repeats));\n\t}\n\n\t/**\n\t * If this {@link Notification} is a repeating reminder, set the {@link #time} to the next\n\t * applicable day, A.K.A. reschedule it. If it is non-repeating, simply delete it.\n\t */\n\tpublic void deleteOrReschedule(final Context context) {\n\t\tif (!this.isRepeating() || time == null) {\n\t\t\t// non-repeating reminder: just delete it\n\t\t\tdelete(context);\n\t\t\treturn;\n\t\t}\n\t\t// repeating reminder: re-schedule it\n\n\t\t// Need to set the correct time, but using today as the date\n\t\t// Because no sense in setting reminders in the past\n\t\tGregorianCalendar gcWasFired = new GregorianCalendar();\n\t\tgcWasFired.setTimeInMillis(time);\n\t\t// Use today's date\n\t\tGregorianCalendar gcToSchedule = new GregorianCalendar();\n\t\tfinal long now = gcToSchedule.getTimeInMillis();\n\t\t// With original time\n\t\tgcToSchedule.set(GregorianCalendar.HOUR_OF_DAY, gcWasFired.get(GregorianCalendar.HOUR_OF_DAY));\n\t\tgcToSchedule.set(GregorianCalendar.MINUTE, gcWasFired.get(GregorianCalendar.MINUTE));\n\t\t// this variable saves a moment in unix milliseconds:\n\t\t// * the day is today, when this function runs\n\t\t// * the hour & minute are those in which the Notification was scheduled to appear\n\t\tfinal long base = gcToSchedule.getTimeInMillis();\n\n\t\t// Check today if the time is actually in the future.\n\t\t// For example if this function runs at \"now\" = 19:30 to reschedule a reminder that was\n\t\t// planned for \"base\" = 19:15, then \"now\" is > \"base\", therefore start = 1\n\t\tfinal int start = now < base ? 0 : 1;\n\t\tboolean done = false;\n\t\tfor (int i = start; i <= 7; i++) {\n\t\t\tif (i!=0) {\n\t\t\t\t// add a day to the hypotized new due date.\n\t\t\t\t// It automatically handles the transitions between ST and DST\n\t\t\t\tgcToSchedule.add(Calendar.DAY_OF_MONTH, 1);\n\t\t\t}\n\n\t\t\t// check if the reminder should repeat on this day\n\t\t\tif (repeatsOn(gcToSchedule.get(GregorianCalendar.DAY_OF_WEEK))) {\n\t\t\t\t// we found the 1° day in which the reminder needs to repeat: save that as the\n\t\t\t\t// new \"due time\" in the database, and we're done.\n\t\t\t\tdone = true;\n\t\t\t\ttime = gcToSchedule.getTimeInMillis();\n\t\t\t\tsave(context);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t}\n\t\t// Just in case of faulty repeat codes\n\t\tif (!done) {\n\t\t\tdelete(context);\n\t\t}\n\t}\n\n\tpublic String getRepeatAsText(final Context context) {\n\t\tfinal StringBuilder sb = new StringBuilder();\n\n\t\tSimpleDateFormat weekDayFormatter = TimeFormatter.getLocalFormatterWeekdayShort(context);\n\t\t// 2013-05-13 was a monday\n\t\tGregorianCalendar gc = new GregorianCalendar(2013, GregorianCalendar.MAY, 13);\n\t\tfinal long base = gc.getTimeInMillis();\n\t\tfinal long day = 24 * 60 * 60 * 1000;\n\t\tfor (int i = 0; i < 7; i++) {\n\t\t\tgc.setTimeInMillis(base + i * day);\n\n\t\t\tif (repeatsOn(gc.get(GregorianCalendar.DAY_OF_WEEK))) {\n\t\t\t\tif (sb.length() > 0) {\n\t\t\t\t\tsb.append(\", \");\n\t\t\t\t}\n\t\t\t\tsb.append(weekDayFormatter.format(gc.getTime()));\n\t\t\t}\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Notifications (=Reminders) can be \"repeating reminders\": they are supposed to re-appear\n\t * in one or more week days\n\t *\n\t * @return TRUE if this {@link Notification} is a repeating reminder\n\t * @implNote See {@link #mon} and {@link #sun}\n\t */\n\tpublic boolean isRepeating() {\n\t\t// \"repeats == 0x1000001\" means that the note repeats on monday and sunday, for example\n\t\treturn this.repeats != 0;\n\t}\n\n\t/**\n\t * a {@link Notification} belongs to a {@link Task} which belongs to a {@link TaskList}\n\t * which can be of 2 types: \"simple notes\" or \"checkable tasks\"\n\t *\n\t * @return TRUE if this reminder is for a \"checkable task\", FALSE if it is for a \"simple note\",\n\t * NULL if it could not be determined\n\t */\n\t@Nullable\n\tpublic Boolean belongsToNoteInListOfTasks(final Context context) {\n\t\tCursor cc = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(TaskList.URI, null, TaskList.Columns._ID + \" IS ?\",\n\t\t\t\t\t\tnew String[] { Long.toString(this.listID) }, null);\n\t\tTaskList result = null;\n\t\tassert cc != null;\n\t\tif (cc.moveToFirst()) result = new TaskList(cc);\n\t\tcc.close();\n\t\tif (result == null) return null;\n\n\t\treturn context.getString(R.string.const_listtype_tasks).equals(result.listtype);\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/RemoteTask.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class RemoteTask extends DAO {\n\n\t// SQL convention says Table name should be \"singular\"\n\tpublic static final String TABLE_NAME = \"remotetask\";\n\n\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.\"\n\t\t\t+ TABLE_NAME;\n\n\tpublic static final Uri URI = Uri.withAppendedPath(\n\t\t\tUri.parse(MyContentProvider.SCHEME + MyContentProvider.AUTHORITY),\n\t\t\tTABLE_NAME);\n\n\tpublic static final int BASEURICODE = 501;\n\tpublic static final int BASEITEMCODE = 502;\n\n\tpublic static void addMatcherUris(UriMatcher sURIMatcher) {\n\t\tsURIMatcher\n\t\t\t\t.addURI(MyContentProvider.AUTHORITY, TABLE_NAME, BASEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME + \"/#\",\n\t\t\t\tBASEITEMCODE);\n\t}\n\n\tpublic static Uri getUri(final long id) {\n\t\treturn Uri.withAppendedPath(URI, Long.toString(id));\n\t}\n\n\tpublic static class Columns implements BaseColumns {\n\n\t\tprivate Columns() {\n\t\t}\n\n\t\tpublic static final String SERVICE = \"service\";\n\t\tpublic static final String ACCOUNT = \"account\";\n\t\tpublic static final String REMOTEID = \"remoteid\";\n\t\tpublic static final String UPDATED = \"updated\";\n\t\tpublic static final String DBID = \"dbid\";\n\t\tpublic static final String LISTDBID = \"listdbid\";\n\t\t// Reserved columns, depending on what different services needs\n\t\tpublic static final String DELETED = \"field1\";\n\t\tpublic static final String FIELD2 = \"field2\";\n\t\tpublic static final String FIELD3 = \"field3\";\n\t\tpublic static final String FIELD4 = \"field4\";\n\t\tpublic static final String FIELD5 = \"field5\";\n\n\t\tpublic static final String[] FIELDS = { _ID, DBID, REMOTEID, UPDATED,\n\t\t\t\tACCOUNT, LISTDBID, DELETED, FIELD2, FIELD3, FIELD4, FIELD5, SERVICE };\n\t}\n\n\t/**\n\t * Main table to store data\n\t */\n\tpublic static final String CREATE_TABLE = \"CREATE TABLE \" +\n\t\t\tTABLE_NAME + \"(\" + Columns._ID +\n\t\t\t\" INTEGER PRIMARY KEY,\" + Columns.ACCOUNT +\n\t\t\t\" TEXT NOT NULL,\" + Columns.SERVICE +\n\t\t\t\" TEXT NOT NULL,\" + Columns.DBID +\n\t\t\t\" INTEGER NOT NULL,\" + Columns.UPDATED +\n\t\t\t\" INTEGER NOT NULL,\" + Columns.REMOTEID +\n\t\t\t\" TEXT NOT NULL,\" + Columns.LISTDBID +\n\t\t\t\" INTEGER NOT NULL,\" + Columns.DELETED +\n\t\t\t\" TEXT,\" + Columns.FIELD2 + \" TEXT,\" +\n\t\t\tColumns.FIELD3 + \" TEXT,\" + Columns.FIELD4 +\n\t\t\t\" TEXT,\" + Columns.FIELD5 + \" TEXT\" +\n\t\t\t// Cant delete on cascade because we must sync before!\n\t\t\t\")\";\n\n\t/*\n\t * Trigger to delete items when their list is deleted\n\t */\n\tpublic static final String TRIGGER_LISTDELETE_CASCADE = \"CREATE TRIGGER cascade_trigger_delete_\" +\n\t\t\tTABLE_NAME + \" AFTER DELETE ON \" +\n\t\t\tRemoteTaskList.TABLE_NAME + \" BEGIN \" +\n\t\t\t\" DELETE FROM \" + TABLE_NAME + \" WHERE \" +\n\t\t\tColumns.LISTDBID + \" IS old.\" +\n\t\t\tRemoteTaskList.Columns.DBID + \" AND \" +\n\t\t\tColumns.ACCOUNT + \" IS old.\" +\n\t\t\tRemoteTaskList.Columns.ACCOUNT + \" AND \" +\n\t\t\tColumns.SERVICE + \" IS old.\" +\n\t\t\tRemoteTaskList.Columns.SERVICE + \";\" + \" END;\";\n\n\t/*\n\t * Trigger to delete items when their real items are deleted\n\t */\n\tpublic static final String TRIGGER_REALDELETE_MARK = \"CREATE TRIGGER trigger_real_deletemark_\" +\n\t\t\tTABLE_NAME + \" AFTER DELETE ON \" +\n\t\t\tTask.TABLE_NAME + \" BEGIN \" + \" UPDATE \" +\n\t\t\tTABLE_NAME + \" SET \" + Columns.DELETED +\n\t\t\t\" = 'deleted' \" + \" WHERE \" + Columns.DBID +\n\t\t\t\" IS old.\" + Task.Columns._ID + \";\" +\n\t\t\t\" END;\";\n\n\t/*\n\t * Trigger to move between lists\n\t */\n\tpublic static final String TRIGGER_MOVE_LIST = \"CREATE TRIGGER trigger_move_list_\" + TABLE_NAME +\n\t\t\t\" AFTER UPDATE OF \" + Task.Columns.DBLIST +\n\t\t\t\" ON \" + Task.TABLE_NAME + \" WHEN old.\" +\n\t\t\tTask.Columns.DBLIST + \" IS NOT new.\" +\n\t\t\tTask.Columns.DBLIST + \" BEGIN \" + \" UPDATE \" +\n\t\t\tTABLE_NAME + \" SET \" + Columns.DELETED +\n\t\t\t\" = 'deleted', \" + Columns.DBID + \" = -99 \" +\n\t\t\t\" WHERE \" + Columns.DBID + \" IS old.\" +\n\t\t\tTask.Columns._ID + \";\" + \" END;\";\n\n\t// milliseconds since 1970-01-01 UTC\n\tpublic Long updated = null;\n\n\tpublic Long dbid = null;\n\tpublic Long listdbid = null;\n\tpublic String account = null;\n\tpublic String remoteId = null;\n\tpublic String deleted = null;\n\tpublic String field2 = null;\n\tpublic String field3 = null;\n\tpublic String field4 = null;\n\tpublic String field5 = null;\n\n\tpublic boolean isDeleted() {\n\t\treturn deleted != null && deleted.equals(\"deleted\");\n\t}\n\n\tpublic void setDeleted(final boolean deleted) {\n\t\tthis.deleted = deleted ? \"deleted\" : null;\n\t}\n\n\t// Should be overwritten by children\n\tpublic String service = null;\n\n\tpublic RemoteTask() {\n\n\t}\n\n\t/**\n\t * None of the fields may be null!\n\t */\n\tpublic RemoteTask(final Long dbid, final Long listdbid,\n\t\t\t\t\t  final String remoteId, final Long updated, final String account) {\n\t\tthis.dbid = dbid;\n\t\tthis.listdbid = listdbid;\n\t\tthis.remoteId = remoteId;\n\t\tthis.updated = updated;\n\t\tthis.account = account;\n\t}\n\n\tpublic RemoteTask(final Cursor c) {\n\t\t_id = c.getLong(0);\n\t\tdbid = c.getLong(1);\n\t\tremoteId = c.getString(2);\n\t\tupdated = c.getLong(3);\n\t\taccount = c.getString(4);\n\t\tlistdbid = c.getLong(5);\n\n\t\tdeleted = c.isNull(6) ? null : c.getString(6);\n\t\tfield2 = c.isNull(7) ? null : c.getString(7);\n\t\tfield3 = c.isNull(8) ? null : c.getString(8);\n\t\tfield4 = c.isNull(9) ? null : c.getString(9);\n\t\tfield5 = c.isNull(10) ? null : c.getString(10);\n\n\t\tservice = c.getString(11);\n\t}\n\n\tpublic RemoteTask(final Uri uri, final ContentValues values) {\n\t\tthis(Long.parseLong(uri.getLastPathSegment()), values);\n\t}\n\n\tpublic RemoteTask(final long id, final ContentValues values) {\n\t\tthis(values);\n\t\t_id = id;\n\t}\n\n\tpublic RemoteTask(final JSONObject json) throws JSONException {\n\t\tif (json.has(Columns.DBID))\n\t\t\tdbid = json.getLong(Columns.DBID);\n\t\tif (json.has(Columns.REMOTEID))\n\t\t\tremoteId = json.getString(Columns.REMOTEID);\n\t\tif (json.has(Columns.UPDATED))\n\t\t\tupdated = json.getLong(Columns.UPDATED);\n\t\tif (json.has(Columns.ACCOUNT))\n\t\t\taccount = json.getString(Columns.ACCOUNT);\n\t\tif (json.has(Columns.SERVICE))\n\t\t\tservice = json.getString(Columns.SERVICE);\n\t\tif (json.has(Columns.LISTDBID))\n\t\t\tlistdbid = json.getLong(Columns.LISTDBID);\n\t\tif (json.has(Columns.DELETED))\n\t\t\tdeleted = json.getString(Columns.DELETED);\n\t\tif (json.has(Columns.FIELD2))\n\t\t\tfield2 = json.getString(Columns.FIELD2);\n\t\tif (json.has(Columns.FIELD3))\n\t\t\tfield3 = json.getString(Columns.FIELD3);\n\t\tif (json.has(Columns.FIELD4))\n\t\t\tfield4 = json.getString(Columns.FIELD4);\n\t\tif (json.has(Columns.FIELD5))\n\t\t\tfield5 = json.getString(Columns.FIELD5);\n\t}\n\n\tpublic RemoteTask(final ContentValues values) {\n\t\tdbid = values.getAsLong(Columns.DBID);\n\t\tremoteId = values.getAsString(Columns.REMOTEID);\n\t\tupdated = values.getAsLong(Columns.UPDATED);\n\t\taccount = values.getAsString(Columns.ACCOUNT);\n\t\tservice = values.getAsString(Columns.SERVICE);\n\t\tlistdbid = values.getAsLong(Columns.LISTDBID);\n\n\t\tdeleted = values.getAsString(Columns.DELETED);\n\t\tfield2 = values.getAsString(Columns.FIELD2);\n\t\tfield3 = values.getAsString(Columns.FIELD3);\n\t\tfield4 = values.getAsString(Columns.FIELD4);\n\t\tfield5 = values.getAsString(Columns.FIELD5);\n\t}\n\n\t@Override\n\tpublic ContentValues getContent() {\n\t\tfinal ContentValues values = new ContentValues();\n\n\t\tvalues.put(Columns.DBID, dbid);\n\t\tvalues.put(Columns.LISTDBID, listdbid);\n\t\tvalues.put(Columns.REMOTEID, remoteId);\n\t\tvalues.put(Columns.UPDATED, updated);\n\t\tvalues.put(Columns.ACCOUNT, account);\n\t\tvalues.put(Columns.SERVICE, service);\n\t\tvalues.put(Columns.DELETED, deleted);\n\t\tvalues.put(Columns.FIELD2, field2);\n\t\tvalues.put(Columns.FIELD3, field3);\n\t\tvalues.put(Columns.FIELD4, field4);\n\t\tvalues.put(Columns.FIELD5, field5);\n\n\t\treturn values;\n\n\t}\n\n\t@Override\n\tprotected String getTableName() {\n\t\treturn TABLE_NAME;\n\t}\n\n\t@Override\n\tpublic String getContentType() {\n\t\treturn CONTENT_TYPE;\n\t}\n\n\t@Override\n\tpublic int save(final Context context) {\n\t\tint result = 0;\n\t\tif (_id < 1) {\n\t\t\tfinal Uri uri = context.getContentResolver().insert(getBaseUri(),\n\t\t\t\t\tgetContent());\n\t\t\tif (uri != null) {\n\t\t\t\t_id = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult++;\n\t\t\t}\n\t\t} else {\n\t\t\tresult += context.getContentResolver().update(getUri(),\n\t\t\t\t\tgetContent(), null, null);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Returns a where clause that can be used to fetch the task that is\n\t * associated with this remote object. As argument, use remoteid, account\n\t */\n\tpublic String getTaskWithRemoteClause() {\n\t\treturn Task.Columns.DBLIST + \" IS ? AND \" +\n\t\t\t\tBaseColumns._ID + \" IN (SELECT \" +\n\t\t\t\tColumns.DBID + \" FROM \" + TABLE_NAME +\n\t\t\t\t\" WHERE \" + Columns.REMOTEID +\n\t\t\t\t\" IS ? AND \" + Columns.ACCOUNT + \" IS ?)\";\n\t}\n\n\tpublic String[] getTaskWithRemoteArgs() {\n\t\treturn new String[] { Long.toString(listdbid), remoteId, account };\n\t}\n\n\t/**\n\t * Returns a where clause that limits the tasklists to those that do not\n\t * have a remote version.\n\t *\n\t * Combine with account\n\t */\n\tpublic static String getTaskWithoutRemoteClause() {\n\t\treturn Task.Columns.DBLIST + \" IS ? AND \" +\n\t\t\t\tBaseColumns._ID + \" NOT IN (SELECT \" +\n\t\t\t\tColumns.DBID + \" FROM \" + TABLE_NAME +\n\t\t\t\t\" WHERE \" + Columns.ACCOUNT + \" IS ? AND \" +\n\t\t\t\tColumns.SERVICE + \" IS ?)\";\n\t}\n\n\tpublic static String[] getTaskWithoutRemoteArgs(final long listdbid,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfinal String account, final String service) {\n\t\treturn new String[] { Long.toString(listdbid), account, service };\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/RemoteTaskList.java",
    "content": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class RemoteTaskList extends DAO {\n\n\t// SQL convention says Table name should be \"singular\"\n\tpublic static final String TABLE_NAME = \"remotetasklist\";\n\n\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.\"\n\t\t\t+ TABLE_NAME;\n\n\tpublic static final Uri URI = Uri.withAppendedPath(\n\t\t\tUri.parse(MyContentProvider.SCHEME + MyContentProvider.AUTHORITY),\n\t\t\tTABLE_NAME);\n\n\tpublic static final int BASEURICODE = 401;\n\tpublic static final int BASEITEMCODE = 402;\n\n\tpublic static void addMatcherUris(UriMatcher sURIMatcher) {\n\t\tsURIMatcher\n\t\t\t\t.addURI(MyContentProvider.AUTHORITY, TABLE_NAME, BASEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME + \"/#\",\n\t\t\t\tBASEITEMCODE);\n\t}\n\n\tpublic static Uri getUri(final long id) {\n\t\treturn Uri.withAppendedPath(URI, Long.toString(id));\n\t}\n\n\tpublic static class Columns implements BaseColumns {\n\n\t\tprivate Columns() {\n\t\t}\n\n\t\tpublic static final String SERVICE = \"service\";\n\t\tpublic static final String ACCOUNT = \"account\";\n\t\tpublic static final String REMOTEID = \"remoteid\";\n\t\tpublic static final String UPDATED = \"updated\";\n\t\tpublic static final String DBID = \"dbid\";\n\t\t// Reserved columns, depending on what different services needs\n\t\tpublic static final String DELETED = \"field1\";\n\t\tpublic static final String FIELD2 = \"field2\";\n\t\tpublic static final String FIELD3 = \"field3\";\n\t\tpublic static final String FIELD4 = \"field4\";\n\t\tpublic static final String FIELD5 = \"field5\";\n\n\t\tpublic static final String[] FIELDS = { _ID, DBID, REMOTEID,\n\t\t\t\tUPDATED, ACCOUNT, DELETED, FIELD2,\n\t\t\t\tFIELD3, FIELD4, FIELD5, SERVICE };\n\t}\n\n\t/**\n\t * Main table to store data\n\t */\n\tpublic static final String CREATE_TABLE = \"CREATE TABLE \" +\n\t\t\tTABLE_NAME +\n\t\t\t\"(\" + Columns._ID + \" INTEGER PRIMARY KEY,\" +\n\t\t\tColumns.ACCOUNT + \" TEXT NOT NULL,\" +\n\t\t\tColumns.SERVICE + \" TEXT NOT NULL,\" +\n\t\t\tColumns.DBID + \" INTEGER NOT NULL,\" +\n\t\t\tColumns.UPDATED + \" INTEGER NOT NULL,\" +\n\t\t\tColumns.REMOTEID + \" TEXT NOT NULL,\" +\n\t\t\tColumns.DELETED + \" TEXT,\" +\n\t\t\tColumns.FIELD2 + \" TEXT,\" +\n\t\t\tColumns.FIELD3 + \" TEXT,\" +\n\t\t\tColumns.FIELD4 + \" TEXT,\" +\n\t\t\tColumns.FIELD5 + \" TEXT\" +\n\t\t\t// Cant delete on cascade, since then we cant remember to sync it!\n\t\t\t\")\";\n\n\t// milliseconds since 1970-01-01 UTC\n\tpublic Long updated = null;\n\n\tpublic Long dbid = null;\n\tpublic String account = null;\n\tpublic String remoteId = null;\n\tpublic String deleted = null;\n\tpublic String field2 = null;\n\tpublic String field3 = null;\n\tpublic String field4 = null;\n\tpublic String field5 = null;\n\n\t// Should be overwritten by children\n\tpublic String service = null;\n\n\tpublic RemoteTaskList() {\n\n\t}\n\n\t/**\n\t * None of the fields may be null!\n\t */\n\tpublic RemoteTaskList(final Long dbid, final String remoteId, final Long updated,\n\t\t\t\t\t\t  final String account) {\n\t\tthis.dbid = dbid;\n\t\tthis.remoteId = remoteId;\n\t\tthis.updated = updated;\n\t\tthis.account = account;\n\t}\n\n\tpublic RemoteTaskList(final Cursor c) {\n\t\t_id = c.getLong(0);\n\t\tdbid = c.getLong(1);\n\t\tremoteId = c.getString(2);\n\t\tupdated = c.getLong(3);\n\t\taccount = c.getString(4);\n\n\t\tdeleted = c.isNull(5) ? null : c.getString(5);\n\t\tfield2 = c.isNull(6) ? null : c.getString(6);\n\t\tfield3 = c.isNull(7) ? null : c.getString(7);\n\t\tfield4 = c.isNull(8) ? null : c.getString(8);\n\t\tfield5 = c.isNull(9) ? null : c.getString(9);\n\n\t\tservice = c.getString(10);\n\t}\n\n\tpublic RemoteTaskList(final Uri uri, final ContentValues values) {\n\t\tthis(Long.parseLong(uri.getLastPathSegment()), values);\n\t}\n\n\tpublic RemoteTaskList(final long id, final ContentValues values) {\n\t\tthis(values);\n\t\t_id = id;\n\t}\n\n\tpublic RemoteTaskList(final JSONObject json) throws JSONException {\n\t\tif (json.has(Columns.DBID))\n\t\t\tdbid = json.getLong(Columns.DBID);\n\t\tif (json.has(Columns.REMOTEID))\n\t\t\tremoteId = json.getString(Columns.REMOTEID);\n\t\tif (json.has(Columns.UPDATED))\n\t\t\tupdated = json.getLong(Columns.UPDATED);\n\t\tif (json.has(Columns.ACCOUNT))\n\t\t\taccount = json.getString(Columns.ACCOUNT);\n\t\tif (json.has(Columns.SERVICE))\n\t\t\tservice = json.getString(Columns.SERVICE);\n\t\tif (json.has(Columns.DELETED))\n\t\t\tdeleted = json.getString(Columns.DELETED);\n\t\tif (json.has(Columns.FIELD2))\n\t\t\tfield2 = json.getString(Columns.FIELD2);\n\t\tif (json.has(Columns.FIELD3))\n\t\t\tfield3 = json.getString(Columns.FIELD3);\n\t\tif (json.has(Columns.FIELD4))\n\t\t\tfield4 = json.getString(Columns.FIELD4);\n\t\tif (json.has(Columns.FIELD5))\n\t\t\tfield5 = json.getString(Columns.FIELD5);\n\t}\n\n\tpublic RemoteTaskList(final ContentValues values) {\n\t\tdbid = values.getAsLong(Columns.DBID);\n\t\tremoteId = values.getAsString(Columns.REMOTEID);\n\t\tupdated = values.getAsLong(Columns.UPDATED);\n\t\taccount = values.getAsString(Columns.ACCOUNT);\n\t\tservice = values.getAsString(Columns.SERVICE);\n\n\t\tdeleted = values.getAsString(Columns.DELETED);\n\t\tfield2 = values.getAsString(Columns.FIELD2);\n\t\tfield3 = values.getAsString(Columns.FIELD3);\n\t\tfield4 = values.getAsString(Columns.FIELD4);\n\t\tfield5 = values.getAsString(Columns.FIELD5);\n\t}\n\n\tpublic boolean isDeleted() {\n\t\treturn deleted != null && !deleted.isEmpty();\n\t}\n\n\tpublic void setDeleted(final boolean deleted) {\n\t\tthis.deleted = deleted ? \"deleted\" : null;\n\t}\n\n\t@Override\n\tpublic ContentValues getContent() {\n\t\tfinal ContentValues values = new ContentValues();\n\n\t\tvalues.put(Columns.DBID, dbid);\n\t\tvalues.put(Columns.REMOTEID, remoteId);\n\t\tvalues.put(Columns.UPDATED, updated);\n\t\tvalues.put(Columns.ACCOUNT, account);\n\t\tvalues.put(Columns.SERVICE, service);\n\t\tvalues.put(Columns.DELETED, deleted);\n\t\tvalues.put(Columns.FIELD2, field2);\n\t\tvalues.put(Columns.FIELD3, field3);\n\t\tvalues.put(Columns.FIELD4, field4);\n\t\tvalues.put(Columns.FIELD5, field5);\n\n\t\treturn values;\n\n\t}\n\n\t@Override\n\tprotected String getTableName() {\n\t\treturn TABLE_NAME;\n\t}\n\n\t@Override\n\tpublic String getContentType() {\n\t\treturn CONTENT_TYPE;\n\t}\n\n\t@Override\n\tpublic int save(final Context context) {\n\t\tint result = 0;\n\t\tif (_id < 1) {\n\t\t\tfinal Uri uri = context.getContentResolver().insert(getBaseUri(),\n\t\t\t\t\tgetContent());\n\t\t\tif (uri != null) {\n\t\t\t\t_id = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult++;\n\t\t\t}\n\t\t} else {\n\t\t\tresult += context.getContentResolver().update(getUri(),\n\t\t\t\t\tgetContent(), null, null);\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic int save(final Context context, final long updateTime) {\n\t\tupdated = updateTime;\n\t\treturn save(context);\n\t}\n\n\t/**\n\t * Returns a where clause that can be used to fetch the tasklist that\n\t * is associated with this remote object.\n\t * As argument, use remoteid, account, service\n\t */\n\tpublic String getTaskListWithRemoteClause() {\n\t\treturn BaseColumns._ID + \" IN (SELECT \" +\n\t\t\t\tColumns.DBID + \" FROM \" + TABLE_NAME + \" WHERE \" +\n\t\t\t\tColumns.REMOTEID + \" IS ? AND \" +\n\t\t\t\tColumns.ACCOUNT + \" IS ? AND \" +\n\t\t\t\tColumns.SERVICE + \" IS ?)\";\n\t}\n\n\tpublic String[] getTaskListWithRemoteArgs() {\n\t\treturn new String[] { remoteId, account, service };\n\t}\n\n\t/**\n\t * Returns a where clause that limits the tasklists to those that do not\n\t * have a remote version.\n\t *\n\t * Combine with account, service\n\t */\n\tpublic static String getTaskListWithoutRemoteClause() {\n\t\treturn BaseColumns._ID + \" NOT IN (SELECT \" +\n\t\t\t\tColumns.DBID + \" FROM \" + TABLE_NAME + \" WHERE \" +\n\t\t\t\tColumns.ACCOUNT + \" IS ? AND \" +\n\t\t\t\tColumns.SERVICE + \" IS ?)\";\n\t}\n\n\tpublic String[] getTaskListWithoutRemoteArgs() {\n\t\treturn new String[] { account, service };\n\t}\n\n\t/*\n\t * Trigger to delete items when their list is deleted\n\t */\n\tpublic static final String TRIGGER_REALDELETE_MARK = \"CREATE TRIGGER trigger_real_deletemark_\" + TABLE_NAME +\n\t\t\t\" AFTER DELETE ON \" + TaskList.TABLE_NAME + \" BEGIN \" +\n\t\t\t\" UPDATE \" + TABLE_NAME + \" SET \" + Columns.DELETED + \" = 'deleted' \" +\n\t\t\t\" WHERE \" + Columns.DBID + \" IS old.\" + TaskList.Columns._ID +\n\t\t\t\";\" +\n\t\t\t\" END;\";\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/Task.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.app.SearchManager;\nimport android.content.ContentResolver;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\n\nimport androidx.annotation.NonNull;\n\nimport com.mobeta.android.dslv.DragSortListView;\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.security.InvalidParameterException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Objects;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * An object that represents the task information contained in the database.\n * Provides convenience methods for moving items.\n */\npublic class Task extends DAO {\n\n\t// Used to separate tasks with due dates from completed and from tasks with\n\t// no date\n\tpublic static final String SECRET_TYPEID = \"secret_typeid\";\n\tpublic static final String SECRET_TYPEID2 = \"secret_typeid2\";\n\n\t// SQL convention says Table name should be \"singular\"\n\tpublic static final String TABLE_NAME = \"task\";\n\tpublic static final String DELETE_TABLE_NAME = \"deleted_task\";\n\tpublic static final String FTS3_DELETE_TABLE_NAME = \"fts3_deleted_task\";\n\tpublic static final String HISTORY_TABLE_NAME = \"history\";\n\tprivate static final String SECTIONED_DATE_VIEW = \"sectioned_date_view\";\n\tpublic static final String FTS3_TABLE_NAME = \"fts3_task\";\n\n\tpublic static String getSECTION_DATE_VIEW_NAME(final String listId) {\n\t\t// listId CAN be null. Hence the string concat hack\n\t\treturn SECTIONED_DATE_VIEW + \"_\" + listId;\n\t}\n\n\t// Used in sectioned view date\n\tstatic final String FAR_FUTURE = \"strftime('%s','3999-01-01') * 1000\";\n\tpublic static final String OVERDUE = \"strftime('%s', '1970-01-01') * 1000\";\n\n\t// Today should be from NOW...\n\tpublic static final String TODAY_START = \"strftime('%s','now', 'utc') * 1000\";\n\n\t/**\n\t * A constraint which is an unix time (in ms) representing midnight of the day that is\n\t * \"offset\" days after today. For example, Running {@code TODAY_PLUS(4)} on 04 jan 2023\n\t * will return a {@code strftime(...)} that SQLite will evaluate to \"1673132400000\", which\n\t * represents 00:00 of 8 jan 2023 in the user's local timezone\n\t *\n\t * @param offset in days\n\t * @return a SQL string that will eventually be evaluated to something like \"1673132400000\"\n\t */\n\tpublic static String TODAY_PLUS(final int offset) {\n\t\treturn \"strftime('%s','now','localtime','+\" + offset\n\t\t\t\t+ \" days','start of day', 'utc') * 1000\";\n\t}\n\n\t// Code used to decode title of date header\n\tpublic static final String HEADER_KEY_TODAY = \"today+0\";\n\tpublic static final String HEADER_KEY_PLUS1 = \"today+1\";\n\tpublic static final String HEADER_KEY_PLUS2 = \"today+2\";\n\tpublic static final String HEADER_KEY_PLUS3 = \"today+3\";\n\tpublic static final String HEADER_KEY_PLUS4 = \"today+4\";\n\tpublic static final String HEADER_KEY_NEXT_MONTH = \"next_month\";\n\tpublic static final String HEADER_KEY_NEXT_YEAR = \"next_year\";\n\n\tpublic static final String HEADER_KEY_OVERDUE = \"overdue\";\n\tpublic static final String HEADER_KEY_LATER = \"later\";\n\tpublic static final String HEADER_KEY_NODATE = \"nodate\";\n\tpublic static final String HEADER_KEY_COMPLETE = \"complete\";\n\n\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.note\";\n\n\tpublic static final Uri URI = Uri.withAppendedPath(\n\t\t\tUri.parse(MyContentProvider.SCHEME + MyContentProvider.AUTHORITY),\n\t\t\tTABLE_NAME);\n\n\tpublic static Uri getUri(final long id) {\n\t\treturn Uri.withAppendedPath(URI, Long.toString(id));\n\t}\n\n\tpublic static final int BASEURICODE = 201;\n\tpublic static final int BASEITEMCODE = 202;\n\tpublic static final int DELETEDQUERYCODE = 209;\n\tpublic static final int DELETEDITEMCODE = 210;\n\tpublic static final int SECTIONEDDATEQUERYCODE = 211;\n\tpublic static final int SECTIONEDDATEITEMCODE = 212;\n\tpublic static final int HISTORYQUERYCODE = 213;\n\tpublic static final int MOVEITEMLEFTCODE = 214;\n\tpublic static final int MOVEITEMRIGHTCODE = 215;\n\n\t// Legacy support, these also need to use legacy projections\n\tpublic static final int LEGACYBASEURICODE = 221;\n\tpublic static final int LEGACYBASEITEMCODE = 222;\n\tpublic static final int LEGACYVISIBLEURICODE = 223;\n\tpublic static final int LEGACYVISIBLEITEMCODE = 224;\n\n\t// Search URI\n\tpublic static final int SEARCHCODE = 299;\n\tpublic static final int SEARCHSUGGESTIONSCODE = 298;\n\n\tpublic static void addMatcherUris(UriMatcher sURIMatcher) {\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME, BASEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME + \"/#\", BASEITEMCODE);\n\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tTABLE_NAME + \"/\" + MOVEITEMLEFT + \"/#\", MOVEITEMLEFTCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tTABLE_NAME + \"/\" + MOVEITEMRIGHT + \"/#\", MOVEITEMRIGHTCODE);\n\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tTABLE_NAME + \"/\" + DELETEDQUERY, DELETEDQUERYCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tTABLE_NAME + \"/\" + DELETEDQUERY + \"/#\", DELETEDITEMCODE);\n\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tTABLE_NAME + \"/\" + SECTIONED_DATE_VIEW, SECTIONEDDATEQUERYCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tTABLE_NAME + \"/\" + SECTIONED_DATE_VIEW + \"/#\", SECTIONEDDATEITEMCODE);\n\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tTABLE_NAME + \"/\" + HISTORY_TABLE_NAME, HISTORYQUERYCODE);\n\n\t\t// Legacy URIs\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Notes.NOTES, LEGACYBASEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Notes.NOTES + \"/#\", LEGACYBASEITEMCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Notes.VISIBLE_NOTES,\n\t\t\t\tLEGACYVISIBLEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Notes.VISIBLE_NOTES + \"/#\",\n\t\t\t\tLEGACYVISIBLEITEMCODE);\n\n\t\t// Search URI\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, FTS3_TABLE_NAME, SEARCHCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tSearchManager.SUGGEST_URI_PATH_QUERY, SEARCHSUGGESTIONSCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tSearchManager.SUGGEST_URI_PATH_QUERY + \"/*\", SEARCHSUGGESTIONSCODE);\n\n\t}\n\n\tpublic static final String TARGETPOS = \"targetpos\";\n\tprivate static final String MOVEITEMLEFT = \"moveitemleft\";\n\tprivate static final String MOVEITEMRIGHT = \"moveitemright\";\n\tprivate static final String DELETEDQUERY = \"deletedquery\";\n\n\t// Special URI to look at backup table\n\tpublic static final Uri URI_DELETED_QUERY = Uri.withAppendedPath(URI, DELETEDQUERY);\n\n\t// Query the view with date section headers\n\tpublic static final Uri URI_SECTIONED_BY_DATE = Uri.withAppendedPath(URI, SECTIONED_DATE_VIEW);\n\n\t// Query for history of tasks\n\tpublic static final Uri URI_TASK_HISTORY = Uri.withAppendedPath(URI, HISTORY_TABLE_NAME);\n\n\t// Search URI\n\tpublic static final Uri URI_SEARCH = Uri.withAppendedPath(\n\t\t\tUri.parse(MyContentProvider.SCHEME + MyContentProvider.AUTHORITY),\n\t\t\tFTS3_TABLE_NAME);\n\n\t// Special URI to use when a move is requested\n\tprivate static final Uri URI_WRITE_MOVEITEMLEFT = Uri.withAppendedPath(URI, MOVEITEMLEFT);\n\tprivate static final Uri URI_WRITE_MOVEITEMRIGHT = Uri.withAppendedPath(URI, MOVEITEMRIGHT);\n\n\tprivate Uri getMoveItemLeftUri() {\n\t\tif (_id < 1) {\n\t\t\tthrow new InvalidParameterException(\"_ID of this object is not valid\");\n\t\t}\n\t\treturn Uri.withAppendedPath(URI_WRITE_MOVEITEMLEFT, Long.toString(_id));\n\t}\n\n\tprivate Uri getMoveItemRightUri() {\n\t\tif (_id < 1) {\n\t\t\tthrow new InvalidParameterException(\"_ID of this object is not valid\");\n\t\t}\n\t\treturn Uri.withAppendedPath(URI_WRITE_MOVEITEMRIGHT, Long.toString(_id));\n\t}\n\n\t/**\n\t * Contains each column of the SQLite table that contains {@link Task} objects,\n\t * and functions to return them as lists\n\t */\n\tpublic static class Columns implements BaseColumns {\n\n\t\tprivate Columns() {}\n\n\t\tpublic static final String TITLE = \"title\";\n\t\tpublic static final String NOTE = \"note\";\n\t\tpublic static final String DBLIST = \"dblist\";\n\t\tpublic static final String COMPLETED = \"completed\";\n\t\tpublic static final String DUE = \"due\";\n\t\tpublic static final String UPDATED = \"updated\";\n\t\tpublic static final String LOCKED = \"locked\";\n\n\t\tpublic static final String LEFT = \"lft\";\n\t\tpublic static final String RIGHT = \"rgt\";\n\n\t\tpublic static final String[] FIELDS = { _ID, TITLE, NOTE, COMPLETED,\n\t\t\t\tDUE, UPDATED, LEFT, RIGHT, DBLIST, LOCKED };\n\t\tpublic static final String[] FIELDS_NO_ID = { TITLE, NOTE, COMPLETED,\n\t\t\t\tDUE, UPDATED, LEFT, RIGHT, DBLIST, LOCKED };\n\t\tpublic static final String[] SHALLOWFIELDS = { _ID, TITLE, NOTE,\n\t\t\t\tDBLIST, COMPLETED, DUE, UPDATED, LOCKED };\n\t\tpublic static final String TRIG_DELETED = \"deletedtime\";\n\t\tpublic static final String HIST_TASK_ID = \"taskid\";\n\t\t// Used to read the table. Deleted field set by database\n\t\tpublic static final String[] DELETEFIELDS = { _ID, TITLE, NOTE,\n\t\t\t\tCOMPLETED, DUE, DBLIST, TRIG_DELETED };\n\t\t// Used in trigger creation\n\t\tprivate static final String[] DELETEFIELDS_TRIGGER = { TITLE, NOTE,\n\t\t\t\tCOMPLETED, DUE, DBLIST };\n\n\t\t// accessible fields in history table\n\t\tpublic static final String[] HISTORY_COLUMNS = { Columns.HIST_TASK_ID,\n\t\t\t\tColumns.TITLE, Columns.NOTE };\n\t\tpublic static final String[] HISTORY_COLUMNS_UPDATED = { Columns.HIST_TASK_ID,\n\t\t\t\tColumns.TITLE, Columns.NOTE, Columns.UPDATED };\n\n\t}\n\n\tpublic static final String CREATE_TABLE = \"CREATE TABLE \" + TABLE_NAME + \"(\" + Columns._ID +\n\t\t\t\" INTEGER PRIMARY KEY,\" + Columns.TITLE + \" TEXT NOT NULL DEFAULT '',\" +\n\t\t\tColumns.NOTE + \" TEXT NOT NULL DEFAULT '',\" +\n\t\t\t// These are all msec times\n\t\t\tColumns.COMPLETED + \" INTEGER DEFAULT NULL,\" + Columns.UPDATED +\n\t\t\t\" INTEGER DEFAULT NULL,\" + Columns.DUE + \" INTEGER DEFAULT NULL,\" +\n\t\t\t// boolean, 1 for locked, unlocked otherwise\n\t\t\tColumns.LOCKED + \" INTEGER NOT NULL DEFAULT 0,\" +\n\n\t\t\t// position stuff\n\t\t\tColumns.LEFT + \" INTEGER NOT NULL DEFAULT 1,\" + Columns.RIGHT +\n\t\t\t\" INTEGER NOT NULL DEFAULT 2,\" + Columns.DBLIST + \" INTEGER NOT NULL,\" +\n\n\t\t\t// Positions must be positive and ordered!\n\t\t\t\" CHECK(\" + Columns.LEFT + \" > 0), \" + \" CHECK(\" + Columns.RIGHT + \" > 1), \" +\n\n\t\t\t// Each side's value should be unique in it's list\n\t\t\t// Handled in trigger\n\t\t\t// + \" UNIQUE(\" + Columns.LEFT + \", \" + Columns.DBLIST + \")\"\n\t\t\t// + \" UNIQUE(\" + Columns.RIGHT + \", \" + Columns.DBLIST + \")\"\n\n\t\t\t// Foreign key for list\n\t\t\t\"FOREIGN KEY(\" + Columns.DBLIST + \") REFERENCES \" + TaskList.TABLE_NAME + \"(\" +\n\t\t\tTaskList.Columns._ID + \") ON DELETE CASCADE\" + \")\";\n\n\t/**\n\t * Delete table has no constraints. In fact, list values and positions should not even be\n\t * thought of as valid\n\t */\n\tpublic static final String CREATE_DELETE_TABLE = \"CREATE TABLE \" +\n\t\t\tDELETE_TABLE_NAME + \"(\" +\n\t\t\tColumns._ID + \" INTEGER PRIMARY KEY,\" +\n\t\t\tColumns.TITLE + \" TEXT NOT NULL DEFAULT '',\" +\n\t\t\tColumns.NOTE + \" TEXT NOT NULL DEFAULT '',\" +\n\t\t\tColumns.COMPLETED + \" INTEGER DEFAULT NULL,\" +\n\t\t\tColumns.DUE + \" INTEGER DEFAULT NULL,\" +\n\t\t\tColumns.DBLIST + \" INTEGER DEFAULT NULL,\" +\n\t\t\tColumns.TRIG_DELETED +\n\t\t\t\" TIMESTAMP NOT NULL DEFAULT current_timestamp\" +\n\t\t\t\")\";\n\n\t/**\n\t * Every change to a note gets saved here\n\t */\n\tpublic static final String CREATE_HISTORY_TABLE = \"CREATE TABLE \" +\n\t\t\tHISTORY_TABLE_NAME + \"(\" +\n\t\t\tColumns._ID + \" INTEGER PRIMARY KEY,\" +\n\t\t\tColumns.HIST_TASK_ID + \" INTEGER NOT NULL,\" +\n\t\t\tColumns.TITLE + \" TEXT NOT NULL DEFAULT '',\" +\n\t\t\tColumns.NOTE + \" TEXT NOT NULL DEFAULT '',\" +\n\t\t\tColumns.UPDATED +\n\t\t\t\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\" +\n\t\t\t\" FOREIGN KEY(\" + Columns.HIST_TASK_ID +\n\t\t\t\" ) REFERENCES \" + TABLE_NAME + \" ( \" +\n\t\t\tColumns._ID + \") ON DELETE CASCADE \" + \" ) \";\n\n\tstatic final String HISTORY_TRIGGER_BODY = \" INSERT INTO \" + HISTORY_TABLE_NAME + \" (\" +\n\t\t\tarrayToCommaString(Columns.HISTORY_COLUMNS) + \")\" + \" VALUES (\" +\n\t\t\tarrayToCommaString(\"new.\",\n\t\t\t\t\tnew String[] { Columns._ID, Columns.TITLE, Columns.NOTE }) + \");\";\n\n\tpublic static final String HISTORY_UPDATE_TRIGGER_NAME = \"trigger_update_\" + HISTORY_TABLE_NAME;\n\tpublic static final String CREATE_HISTORY_UPDATE_TRIGGER = \"CREATE TRIGGER \" +\n\t\t\tHISTORY_UPDATE_TRIGGER_NAME + \" AFTER UPDATE OF \" +\n\t\t\tarrayToCommaString(Columns.TITLE, Columns.NOTE) + \" ON \" + TABLE_NAME + \" WHEN old.\" +\n\t\t\tColumns.TITLE + \" IS NOT new.\" + Columns.TITLE + \" OR old.\" + Columns.NOTE +\n\t\t\t\" IS NOT new.\" + Columns.NOTE + \" BEGIN \" + HISTORY_TRIGGER_BODY + \" END;\";\n\n\tpublic static final String CREATE_HISTORY_INSERT_TRIGGER = \"CREATE TRIGGER trigger_insert_\" +\n\t\t\tHISTORY_TABLE_NAME + \" AFTER INSERT ON \" + TABLE_NAME + \" BEGIN \" +\n\t\t\tHISTORY_TRIGGER_BODY + \" END;\";\n\n\t// Delete search table\n\tpublic static final String CREATE_FTS3_DELETE_TABLE = \"CREATE VIRTUAL TABLE \"\n\t\t\t+ FTS3_DELETE_TABLE_NAME + \" USING FTS3(\" + Columns._ID + \", \"\n\t\t\t+ Columns.TITLE + \", \" + Columns.NOTE + \");\";\n\n\tpublic static final String CREATE_FTS3_DELETED_INSERT_TRIGGER =\n\t\t\t\"CREATE TRIGGER deletedtask_fts3_insert AFTER INSERT ON \" + DELETE_TABLE_NAME +\n\t\t\t\t\t\" BEGIN \" + \" INSERT INTO \" + FTS3_DELETE_TABLE_NAME + \" (\" +\n\t\t\t\t\tarrayToCommaString(Columns._ID, Columns.TITLE, Columns.NOTE) + \") VALUES (\" +\n\t\t\t\t\tarrayToCommaString(\"new.\",\n\t\t\t\t\t\t\tnew String[] { Columns._ID, Columns.TITLE, Columns.NOTE }) +\n\t\t\t\t\t\");\" + \" END;\";\n\n\tpublic static final String CREATE_FTS3_DELETED_UPDATE_TRIGGER =\n\t\t\t\"CREATE TRIGGER deletedtask_fts3_update AFTER UPDATE OF \" +\n\t\t\t\t\tarrayToCommaString(Columns.TITLE, Columns.NOTE) +\n\t\t\t\t\t\" ON \" +\n\t\t\t\t\tDELETE_TABLE_NAME +\n\t\t\t\t\t\" BEGIN \" +\n\t\t\t\t\t\" UPDATE \" +\n\t\t\t\t\tFTS3_DELETE_TABLE_NAME +\n\t\t\t\t\t\" SET \" +\n\t\t\t\t\tColumns.TITLE + \" = new.\" + Columns.TITLE +\n\t\t\t\t\t\",\" + Columns.NOTE + \" = new.\" +\n\t\t\t\t\tColumns.NOTE + \" WHERE \" + Columns._ID +\n\t\t\t\t\t\" IS new.\" + Columns._ID + \";\" + \" END;\";\n\n\tpublic static final String CREATE_FTS3_DELETED_DELETE_TRIGGER =\n\t\t\t\"CREATE TRIGGER deletedtask_fts3_delete AFTER DELETE ON \" +\n\t\t\t\t\tDELETE_TABLE_NAME + \" BEGIN \" +\n\t\t\t\t\t\" DELETE FROM \" + FTS3_DELETE_TABLE_NAME +\n\t\t\t\t\t\" WHERE \" + Columns._ID + \" IS old.\" +\n\t\t\t\t\tColumns._ID + \";\" + \" END;\";\n\n\t// Search table\n\tpublic static final String CREATE_FTS3_TABLE = \"CREATE VIRTUAL TABLE \"\n\t\t\t+ FTS3_TABLE_NAME + \" USING FTS3(\" + Columns._ID + \", \"\n\t\t\t+ Columns.TITLE + \", \" + Columns.NOTE + \");\";\n\n\tpublic static final String CREATE_FTS3_INSERT_TRIGGER =\n\t\t\t\"CREATE TRIGGER task_fts3_insert AFTER INSERT ON \" +\n\t\t\t\t\tTABLE_NAME +\n\t\t\t\t\t\" BEGIN \" +\n\t\t\t\t\t\" INSERT INTO \" +\n\t\t\t\t\tFTS3_TABLE_NAME +\n\t\t\t\t\t\" (\" +\n\t\t\t\t\tarrayToCommaString(Columns._ID, Columns.TITLE, Columns.NOTE) +\n\t\t\t\t\t\") VALUES (\" +\n\t\t\t\t\tarrayToCommaString(\"new.\", new String[] { Columns._ID,\n\t\t\t\t\t\t\tColumns.TITLE, Columns.NOTE }) +\n\t\t\t\t\t\");\" +\n\t\t\t\t\t\" END;\";\n\n\tpublic static final String CREATE_FTS3_UPDATE_TRIGGER =\n\t\t\t\"CREATE TRIGGER task_fts3_update AFTER UPDATE OF \" +\n\t\t\t\t\tarrayToCommaString(Columns.TITLE, Columns.NOTE) + \" ON \" + TABLE_NAME +\n\t\t\t\t\t\" BEGIN \" + \" UPDATE \" + FTS3_TABLE_NAME + \" SET \" + Columns.TITLE + \" = new.\" +\n\t\t\t\t\tColumns.TITLE + \",\" + Columns.NOTE + \" = new.\" + Columns.NOTE + \" WHERE \" +\n\t\t\t\t\tColumns._ID + \" IS new.\" + Columns._ID + \";\" + \" END;\";\n\n\tpublic static final String CREATE_FTS3_DELETE_TRIGGER =\n\t\t\t\"CREATE TRIGGER task_fts3_delete AFTER DELETE ON \" + TABLE_NAME + \" BEGIN \" +\n\t\t\t\t\t\" DELETE FROM \" + FTS3_TABLE_NAME + \" WHERE \" + Columns._ID + \" IS old.\" +\n\t\t\t\t\tColumns._ID + \";\" + \" END;\";\n\n\t/**\n\t * This is a SQLite view which returns the tasks in the specified list with headers\n\t * suitable for dates, if any tasks would be sorted under them. Headers are used\n\t * in the {@link DragSortListView} when notes are ordered by date.\n\t * Provider hardcodes the sort order for this.\n\t *\n\t * @param listId if it is null, the function will return (a query) for all lists\n\t * @return a SQL query to create this view\n\t */\n\tpublic static String CREATE_SECTIONED_DATE_VIEW(final String listId) {\n\n\t\t// TODO on the API 35 emulator (and on the Google Pixel 8a), this function creates a view\n\t\t//  where the \"dblist\" column is (erroneously) of type BLOB, while on the API 34 emulator\n\t\t//  (and in previous android versions) \"dblist\" correctly maintains the INTEGER TYPE.\n\t\t//  I think it's because we supply sListId and listId with arrayToCommaString(), so\n\t\t//\t we get '1' instead of 1. On older android versions this is ignored, but since API 35\n\t\t//   it becomes a problem.\n\t\tfinal String sListId = listId == null ? \" NOT NULL \" : \"'\" + listId + \"'\";\n\n\t\tString beginning = \"CREATE TEMP VIEW IF NOT EXISTS \" + getSECTION_DATE_VIEW_NAME(listId) +\n\t\t\t\t// Tasks WITH dates NOT completed, secret 0\n\t\t\t\t\" AS SELECT \" + arrayToCommaString(Columns.FIELDS) + \",0\" + \" AS \" + SECRET_TYPEID +\n\t\t\t\t\",1\" + \" AS \" + SECRET_TYPEID2 + \" FROM \" + TABLE_NAME + \" WHERE \" +\n\t\t\t\tColumns.COMPLETED + \" IS null \" + \" AND \" + Columns.DUE + \" IS NOT null \" +\n\t\t\t\t\" UNION ALL \" +\n\t\t\t\t// Tasks NO dates NOT completed, secret 1\n\t\t\t\t\" SELECT \" + arrayToCommaString(Columns.FIELDS) + \",1\" + \" AS \" + SECRET_TYPEID +\n\t\t\t\t\",1\" + \" AS \" + SECRET_TYPEID2 + \" FROM \" + TABLE_NAME + \" WHERE \" +\n\t\t\t\tColumns.COMPLETED + \" IS null \" + \" AND \" + Columns.DUE + \" IS null \" +\n\t\t\t\t\" UNION ALL \" +\n\t\t\t\t// Tasks completed, secret 2 + 1\n\t\t\t\t\" SELECT \" + arrayToCommaString(Columns.FIELDS) + \",3\" + \" AS \" + SECRET_TYPEID +\n\t\t\t\t\",1\" + \" AS \" + SECRET_TYPEID2 + \" FROM \" + TABLE_NAME + \" WHERE \" +\n\t\t\t\tColumns.COMPLETED + \" IS NOT null \";\n\n\t\tString TODAY = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_START, Columns.TITLE, HEADER_KEY_TODAY, Columns.DBLIST, listId) +\n\t\t\t\t\",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + TODAY_START + \" AND \" + TODAY_PLUS(1) + \") \";\n\n\t\t// TOMORROW = Today + 1\n\t\tString PLUS_1 = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_PLUS(1), Columns.TITLE, HEADER_KEY_PLUS1, Columns.DBLIST,\n\t\t\t\tlistId) + \",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + TODAY_PLUS(1) + \" AND \" + TODAY_PLUS(2) + \") \";\n\n\t\t// Today + 2\n\t\tString PLUS_2 = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_PLUS(2), Columns.TITLE, HEADER_KEY_PLUS2, Columns.DBLIST,\n\t\t\t\tlistId) + \",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + TODAY_PLUS(2) + \" AND \" + TODAY_PLUS(3) + \") \";\n\n\t\t// Today + 3\n\t\tString PLUS_3 = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_PLUS(3), Columns.TITLE, HEADER_KEY_PLUS3, Columns.DBLIST,\n\t\t\t\tlistId) + \",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + TODAY_PLUS(3) + \" AND \" + TODAY_PLUS(4) + \") \";\n\n\t\t// in this function you can add the code to create more headers for when the\n\t\t// notes list is sorted by due date, but I think these are enough already\n\n\t\t// Today + 4\n\t\tString PLUS_4 = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_PLUS(4), Columns.TITLE, HEADER_KEY_PLUS4, Columns.DBLIST,\n\t\t\t\tlistId) + \",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + TODAY_PLUS(4) + \" AND \" + TODAY_PLUS(5) + \") \";\n\n\t\tint daysUntilNextMonth = TimeFormatter.getHowManyDaysUntilFirstOfNextMonth();\n\t\t// the next month will end in (...) days:\n\t\tint toEndOfNextMonth = daysUntilNextMonth + TimeFormatter.getHowManyDaysInTheNextMonth();\n\n\t\t// the goal of this is to add a \"fake note\" in the DATABASE view created by this function.\n\t\t// This \"fake note\" will then show up in the drag-sort-listview (but not as an\n\t\t// user-iteractable note, just a header) to show that the next month starts there.\n\t\t// Any note after that is due after the next month begins\n\t\tString nextMonth = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_PLUS(daysUntilNextMonth), Columns.TITLE, HEADER_KEY_NEXT_MONTH,\n\t\t\t\tColumns.DBLIST, listId) + \",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it, so if there are task with a due\n\t\t\t\t// time in the next month\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + TODAY_PLUS(daysUntilNextMonth) + \" AND \" + TODAY_PLUS(toEndOfNextMonth)\n\t\t\t\t+ \") \";\n\n\t\tint daysUntilNextYear = TimeFormatter.getHowManyDaysUntilFirstOfNextYear();\n\t\tint toEndOfNextYear = daysUntilNextYear + TimeFormatter.getHowManyDaysInNextYear();\n\n\t\tString nextYear = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_PLUS(daysUntilNextYear), Columns.TITLE, HEADER_KEY_NEXT_YEAR,\n\t\t\t\tColumns.DBLIST, listId) + \",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + TODAY_PLUS(daysUntilNextYear) + \" AND \" + TODAY_PLUS(toEndOfNextYear)\n\t\t\t\t+ \") \";\n\n\t\t// Overdue (0)\n\t\tString overdue = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, OVERDUE, Columns.TITLE, HEADER_KEY_OVERDUE, Columns.DBLIST, listId) +\n\t\t\t\t\",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" BETWEEN \" + OVERDUE + \" AND \" + TODAY_START + \") \";\n\n\t\t// Later. As of now, later = \"after the end of the next year\"\n\t\tString later = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, TODAY_PLUS(toEndOfNextYear), Columns.TITLE, HEADER_KEY_LATER, Columns.DBLIST,\n\t\t\t\tlistId) + \",0,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.COMPLETED +\n\t\t\t\t\" IS NULL \" + \" AND \" + Columns.DBLIST + \" IS \" + sListId + \" AND \" + Columns.DUE +\n\t\t\t\t\" >= \" + TODAY_PLUS(toEndOfNextYear) + \") \";\n\n\t\t// No date\n\t\tString noDate = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, \"null\", Columns.TITLE, HEADER_KEY_NODATE, Columns.DBLIST,\n\t\t\t\tlistId) + \",1,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.DBLIST +\n\t\t\t\t\" IS \" + sListId + \" AND \" + Columns.DUE + \" IS null \" + \" AND \" +\n\t\t\t\tColumns.COMPLETED + \" IS null \" + \") \";\n\n\t\t// Complete, overdue to catch all\n\t\t// Set complete time to 1\n\t\tString finalSql = \" UNION ALL \" + \" SELECT -1,\" + asEmptyCommaStringExcept(Columns.FIELDS_NO_ID,\n\t\t\t\tColumns.DUE, OVERDUE, Columns.COMPLETED, \"1\", Columns.TITLE,\n\t\t\t\tHEADER_KEY_COMPLETE, Columns.DBLIST, listId) + \",2,0\" +\n\t\t\t\t// Only show header if there are tasks under it\n\t\t\t\t\" WHERE EXISTS(SELECT _ID FROM \" + TABLE_NAME + \" WHERE \" + Columns.DBLIST +\n\t\t\t\t\" IS \" + sListId + \" AND \" + Columns.COMPLETED + \" IS NOT null \" + \") \" + \";\";\n\n\t\treturn beginning + TODAY + PLUS_1 + PLUS_2 + PLUS_3 + PLUS_4 + nextMonth + nextYear +\n\t\t\t\toverdue + later + noDate + finalSql;\n\t}\n\n\t/**\n\t * Fields of this note\n\t */\n\tpublic String title = null;\n\tpublic String note = null;\n\n\t/**\n\t * When this Task was completed, in milliseconds since 1970-01-01 UTC\n\t */\n\tpublic Long completed = null;\n\n\t/**\n\t * When this Task is due, in milliseconds since 1970-01-01 UTC\n\t */\n\tpublic Long due = null;\n\n\t/**\n\t * When this Task was last updated, in milliseconds since 1970-01-01 UTC\n\t */\n\tpublic Long updated = null;\n\n\t/**\n\t * If this {@link Task} is password-protected. Saved as integer in the database\n\t */\n\tpublic boolean locked = false;\n\n\t// position stuff\n\tpublic Long left = null;\n\tpublic Long right = null;\n\tpublic Long dblist = null;\n\n\tpublic Task() {}\n\n\t/**\n\t * Resets id and position values\n\t */\n\tpublic void resetForInsertion() {\n\t\t_id = -1;\n\t\tleft = null;\n\t\tright = null;\n\t}\n\n\t/**\n\t * Set task as completed. Only used when importing from the legacy DB.\n\t * It seems the intent was to initialize the completed notes imported from the legacy DB\n\t * with a completed timestamp\n\t */\n\tpublic void setAsCompletedForLegacy() {\n\t\t// TODO functions dealing with the legacy DB, including this one, should just be deleted:\n\t\t//  the legacy DB probably dates back to 2012 !\n\t\tcompleted = Calendar.getInstance().getTimeInMillis();\n\t}\n\n\t/**\n\t * @param text this function sets this {@link String}'s first line as title, the rest as\n\t *             content of this {@link Task}, so don't forget the {@code \\n } !\n\t */\n\tpublic void setText(@NonNull final String text) {\n\t\tint titleEnd = text.indexOf(\"\\n\");\n\n\t\tif (titleEnd < 0) {\n\t\t\ttitleEnd = text.length();\n\t\t}\n\n\t\ttitle = text.substring(0, titleEnd);\n\t\tif (titleEnd + 1 < text.length()) {\n\t\t\tnote = text.substring(titleEnd + 1);\n\t\t} else {\n\t\t\tnote = \"\";\n\t\t}\n\t}\n\n\t/**\n\t * Returns a text where first line is title, rest is note\n\t */\n\tpublic String getText() {\n\t\tString result = \"\";\n\t\tif (title != null) {\n\t\t\tresult += title;\n\t\t}\n\t\tif (note != null && !note.isEmpty()) {\n\t\t\tif (!result.isEmpty()) {\n\t\t\t\tresult += \"\\n\";\n\t\t\t}\n\t\t\tresult += note;\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic Task(final Cursor c) {\n\t\tthis._id = c.getLong(0);\n\t\tthis.title = c.getString(1);\n\t\tnote = c.getString(2);\n\t\t// msec times which can be null\n\t\tif (!c.isNull(3)) completed = c.getLong(3);\n\t\tif (!c.isNull(4)) due = c.getLong(4);\n\t\tif (!c.isNull(5)) updated = c.getLong(5);\n\n\t\t// enforced not to be null\n\t\tleft = c.getLong(6);\n\t\tright = c.getLong(7);\n\t\tdblist = c.getLong(8);\n\t\tlocked = c.getInt(9) == 1;\n\t}\n\n\tpublic Task(final long id, final ContentValues values) {\n\t\tthis(values);\n\t\tthis._id = id;\n\t}\n\n\tpublic Task(final Uri uri, final ContentValues values) {\n\t\tthis(Long.parseLong(uri.getLastPathSegment()), values);\n\t}\n\n\tpublic Task(final ContentValues values) {\n\t\tif (values != null) {\n\t\t\tif (values.containsKey(TARGETPOS)) {\n\t\t\t\t// Content form getMoveValues\n\t\t\t\tthis.left = values.getAsLong(Columns.LEFT);\n\t\t\t\tthis.right = values.getAsLong(Columns.RIGHT);\n\t\t\t\tthis.dblist = values.getAsLong(Columns.DBLIST);\n\t\t\t} else {\n\t\t\t\tthis.title = values.getAsString(Columns.TITLE);\n\t\t\t\tthis.note = values.getAsString(Columns.NOTE);\n\t\t\t\tthis.completed = values.getAsLong(Columns.COMPLETED);\n\t\t\t\tthis.due = values.getAsLong(Columns.DUE);\n\t\t\t\tthis.updated = values.getAsLong(Columns.UPDATED);\n\t\t\t\tthis.locked = values.getAsLong(Columns.LOCKED) == 1;\n\n\t\t\t\tthis.dblist = values.getAsLong(Columns.DBLIST);\n\t\t\t\tthis.left = values.getAsLong(Columns.LEFT);\n\t\t\t\tthis.right = values.getAsLong(Columns.RIGHT);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Task(final JSONObject json) throws JSONException {\n\t\tif (json.has(Columns.TITLE))\n\t\t\tthis.title = json.getString(Columns.TITLE);\n\t\tif (json.has(Columns.NOTE))\n\t\t\tthis.note = json.getString(Columns.NOTE);\n\t\tif (json.has(Columns.COMPLETED))\n\t\t\tthis.completed = json.getLong(Columns.COMPLETED);\n\t\tif (json.has(Columns.DUE))\n\t\t\tthis.due = json.getLong(Columns.DUE);\n\t\tif (json.has(Columns.UPDATED))\n\t\t\tthis.updated = json.getLong(Columns.UPDATED);\n\t\tif (json.has(Columns.LOCKED))\n\t\t\tthis.locked = json.getLong(Columns.LOCKED) == 1;\n\t\tif (json.has(Columns.DBLIST))\n\t\t\tthis.dblist = json.getLong(Columns.DBLIST);\n\t\tif (json.has(Columns.LEFT))\n\t\t\tthis.left = json.getLong(Columns.LEFT);\n\t\tif (json.has(Columns.RIGHT))\n\t\t\tthis.right = json.getLong(Columns.RIGHT);\n\t}\n\n\t/**\n\t * A move operation should be performed alone. No other information should\n\t * accompany such an update.\n\t */\n\tpublic ContentValues getMoveValues(final long targetPos) {\n\t\tfinal ContentValues values = new ContentValues();\n\t\tvalues.put(TARGETPOS, targetPos);\n\t\tvalues.put(Columns.LEFT, left);\n\t\tvalues.put(Columns.RIGHT, right);\n\t\tvalues.put(Columns.DBLIST, dblist);\n\t\treturn values;\n\t}\n\n\t/**\n\t * Use this for regular updates of the task.\n\t */\n\t@Override\n\tpublic ContentValues getContent() {\n\t\tfinal ContentValues values = new ContentValues();\n\t\t// Note that ID is NOT included here\n\t\tif (title != null) values.put(Columns.TITLE, title);\n\t\tif (note != null) values.put(Columns.NOTE, note);\n\n\t\tif (dblist != null) values.put(Columns.DBLIST, dblist);\n\n\t\tvalues.put(Columns.UPDATED, updated);\n\t\tvalues.put(Columns.DUE, due);\n\t\tvalues.put(Columns.COMPLETED, completed);\n\t\tvalues.put(Columns.LOCKED, locked ? 1 : 0);\n\n\t\treturn values;\n\t}\n\n\t/**\n\t * Compares this task to another and returns true if their contents are the\n\t * same. Content is defined as: title, note, duedate, completed != null\n\t * Returns false if title or note are null.\n\t * The intended usage is the editor where content and not id's or position\n\t * are of importance.\n\t */\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tboolean result;\n\n\t\tif (o instanceof Task other) {\n\t\t\tresult = true;\n\n\t\t\tresult &= (title != null && title.equals(other.title));\n\t\t\tresult &= (note != null && note.equals(other.note));\n\t\t\tresult &= (Objects.equals(due, other.due));\n\t\t\tresult &= ((completed != null) == (other.completed != null));\n\n\t\t} else {\n\t\t\tresult = super.equals(o);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Convenience method for normal operations. Updates \"updated\" field to\n\t * specified Returns number of db-rows affected. Fail if < 1\n\t */\n\tpublic int save(final Context context, final long updated) {\n\t\tint result = 0;\n\t\tthis.updated = updated;\n\t\tif (_id < 1) {\n\t\t\tfinal Uri uri = context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.insert(getBaseUri(), getContent());\n\t\t\tif (uri != null) {\n\t\t\t\t_id = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult++;\n\t\t\t}\n\t\t} else {\n\t\t\tresult += context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.update(getUri(), getContent(), null, null);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Convenience method for normal operations. Updates \"updated\" field.\n\t * Returns number of db-rows affected. Fail if < 1\n\t */\n\t@Override\n\tpublic int save(final Context context) {\n\t\treturn save(context, Calendar.getInstance().getTimeInMillis());\n\t}\n\n\t/**\n\t * A reusable background thread\n\t */\n\tprivate static final ExecutorService mTaskExecutor = Executors.newSingleThreadExecutor();\n\n\t/**\n\t * Convenience method to complete tasks in list view for example. Works in the background.\n\t */\n\tpublic static void setCompleted(final Context context, final boolean completed,\n\t\t\t\t\t\t\t\t\tfinal Long... ids) {\n\t\tif (ids.length < 1) return;\n\t\tmTaskExecutor.execute(() -> setCompletedSynced(context, completed, ids));\n\t}\n\n\t/**\n\t * Convenience method to complete tasks. Runs on the thread that called it.\n\t */\n\tpublic static void setCompletedSynced(final Context context, final boolean completed,\n\t\t\t\t\t\t\t\t\t\t  final Long... ids) {\n\t\tif (ids.length < 1) return;\n\n\t\tlong thisInstant = Calendar.getInstance().getTimeInMillis();\n\t\tfinal ContentValues values = new ContentValues();\n\t\tvalues.put(Columns.COMPLETED, completed ? thisInstant : null);\n\t\tvalues.put(Columns.UPDATED, thisInstant);\n\n\t\tString idStrings = \"(\";\n\t\tfor (Long id : ids) {\n\t\t\tidStrings += id + \",\";\n\t\t}\n\t\tidStrings = idStrings.substring(0, idStrings.length() - 1);\n\t\tidStrings += \")\";\n\t\tcontext.getContentResolver()\n\t\t\t\t.update(URI, values, Columns._ID + \" IN \" + idStrings, null);\n\t}\n\n\tpublic int moveTo(final ContentResolver resolver, final Task targetTask) {\n\t\tif (targetTask.dblist.equals(dblist)) {\n\t\t\tif (targetTask.left < left) {\n\t\t\t\t// moving left\n\t\t\t\treturn resolver.update(getMoveItemLeftUri(),\n\t\t\t\t\t\tgetMoveValues(targetTask.left), null, null);\n\t\t\t} else if (targetTask.right > right) {\n\t\t\t\t// moving right\n\t\t\t\treturn resolver.update(getMoveItemRightUri(),\n\t\t\t\t\t\tgetMoveValues(targetTask.right), null, null);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected String getTableName() {\n\t\treturn TABLE_NAME;\n\t}\n\n\t/**\n\t * Can't use unique constraint on positions because SQLite checks\n\t * constraints after every row is updated an not after each statement like\n\t * it should. So have to do the check in a trigger instead.\n\t */\n\tstatic String countVals(final String col, final String ver) {\n\t\treturn String.format(\"SELECT COUNT(DISTINCT %2$s)\"\n\t\t\t\t\t\t+ \" AS ColCount FROM %1$s WHERE %3$s=%4$s.%3$s\", TABLE_NAME,\n\t\t\t\tcol, Columns.DBLIST, ver);\n\t}\n\n\t// verify that left are unique\n\t// count number of id and compare to number of left and right\n\tstatic String posUniqueConstraint(final String ver, final String msg) {\n\t\treturn String.format(\n\t\t\t\t\" SELECT CASE WHEN ((%1$s) != (%2$s) OR (%1$s) != (%3$s)) THEN \"\n\t\t\t\t\t\t+ \" RAISE (ABORT, '\" + msg + \"')\" + \" END;\",\n\t\t\t\tcountVals(Columns._ID, ver), countVals(Columns.LEFT, ver),\n\t\t\t\tcountVals(Columns.RIGHT, ver));\n\t}\n\n\t// Makes a gap in the list where the task is being inserted\n\tprivate static final String BUMP_TO_RIGHT =\n\t\t\t\" UPDATE %1$s SET %2$s = %2$s + 2, %3$s = %3$s + 2 WHERE %3$s >= new.%3$s AND %4$s IS new.%4$s;\";\n\n\tpublic static final String TRIGGER_PRE_INSERT = String.format(\n\t\t\t\"CREATE TRIGGER task_pre_insert BEFORE INSERT ON %s BEGIN \",\n\t\t\tTABLE_NAME)\n\t\t\t+ String.format(BUMP_TO_RIGHT, TABLE_NAME, Columns.RIGHT,\n\t\t\tColumns.LEFT, Columns.DBLIST) + \" END;\";\n\n\tpublic static final String TRIGGER_POST_INSERT = String.format(\n\t\t\t\"CREATE TRIGGER task_post_insert AFTER INSERT ON %s BEGIN \",\n\t\t\tTABLE_NAME)\n\t\t\t// Enforce integrity\n\t\t\t+ posUniqueConstraint(\"new\", \"pos not unique post insert\")\n\n\t\t\t+ \" END;\";\n\n\t// Upgrades children and closes the gap made from the delete\n\tprivate static final String BUMP_TO_LEFT =\n\t\t\t\" UPDATE %1$s SET %2$s = %2$s - 2 WHERE %2$s > old.%3$s AND %4$s IS old.%4$s;\";\n\n\tpublic static final String TRIGGER_POST_DELETE = String.format(\n\t\t\t\"CREATE TRIGGER task_post_delete AFTER DELETE ON %s BEGIN \",\n\t\t\tTABLE_NAME)\n\t\t\t// + String.format(UPGRADE_CHILDREN, TABLE_NAME, Columns.LEFT,\n\t\t\t// Columns.RIGHT, Columns.DBLIST)\n\t\t\t+ String.format(BUMP_TO_LEFT, TABLE_NAME, Columns.LEFT,\n\t\t\tColumns.RIGHT, Columns.DBLIST)\n\t\t\t+ String.format(BUMP_TO_LEFT, TABLE_NAME, Columns.RIGHT,\n\t\t\tColumns.RIGHT, Columns.DBLIST)\n\n\t\t\t// Enforce integrity\n\t\t\t+ posUniqueConstraint(\"old\", \"pos not unique post delete\")\n\n\t\t\t+ \" END;\";\n\n\tpublic static final String TRIGGER_PRE_DELETE = String.format(\n\t\t\t\"CREATE TRIGGER task_pre_delete BEFORE DELETE ON %1$s BEGIN \"\n\t\t\t\t\t+ \" INSERT INTO %2$s (\"\n\t\t\t\t\t+ arrayToCommaString(\"\", Columns.DELETEFIELDS_TRIGGER, \"\")\n\t\t\t\t\t+ \") \"\n\t\t\t\t\t+ \" VALUES(\"\n\t\t\t\t\t+ arrayToCommaString(\"old.\", Columns.DELETEFIELDS_TRIGGER,\n\t\t\t\t\t\"\") + \"); \"\n\n\t\t\t\t\t+ \" END;\", TABLE_NAME, DELETE_TABLE_NAME);\n\n\tpublic String getSQLMoveItemLeft(final ContentValues values) {\n\t\tif (!values.containsKey(TARGETPOS) || values.getAsLong(TARGETPOS) >= left) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getSQLMoveItem(Columns.LEFT, values.getAsLong(TARGETPOS));\n\t}\n\n\tpublic String getSQLMoveItemRight(final ContentValues values) {\n\t\tif (!values.containsKey(TARGETPOS) || values.getAsLong(TARGETPOS) <= right) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getSQLMoveItem(Columns.RIGHT, values.getAsLong(TARGETPOS));\n\t}\n\n\t/*\n\t * Trigger to move between lists\n\t */\n\tpublic static final String TRIGGER_MOVE_LIST = \"CREATE TRIGGER trigger_post_move_list_\" +\n\t\t\tTABLE_NAME + \" AFTER UPDATE OF \" + Columns.DBLIST + \" ON \" + Task.TABLE_NAME +\n\t\t\t\" WHEN old.\" + Columns.DBLIST + \" IS NOT new.\" + Columns.DBLIST + \" BEGIN \" +\n\t\t\t// Bump everything to the right, except the item itself (in same list)\n\t\t\tString.format(\"UPDATE %1$s SET %2$s = %2$s + 2, %3$s = %3$s + 2 WHERE %4$s IS new.%4$s AND %5$s IS NOT new.%5$s;\",\n\t\t\t\t\tTABLE_NAME, Columns.LEFT, Columns.RIGHT, Columns.DBLIST, Columns._ID) +\n\n\t\t\t// Bump everything left in the old list, to the right of position\n\t\t\tString.format(\"UPDATE %1$s SET %2$s = %2$s - 2, %3$s = %3$s - 2 WHERE %2$s > old.%3$s AND %4$s IS old.%4$s;\",\n\t\t\t\t\tTABLE_NAME, Columns.LEFT, Columns.RIGHT, Columns.DBLIST) +\n\n\t\t\t// Set positions to 1 and 2 for item\n\t\t\tString.format(\"UPDATE %1$s SET %2$s = 1, %3$s = 2 WHERE %4$s IS new.%4$s;\",\n\t\t\t\t\tTABLE_NAME, Columns.LEFT, Columns.RIGHT, Columns._ID) +\n\t\t\tposUniqueConstraint(\"new\", \"Moving list, new positions not unique/ordered\") +\n\t\t\tposUniqueConstraint(\"old\", \"Moving list, old positions not unique/ordered\") +\n\t\t\t\" END;\";\n\n\t/**\n\t * If moving left, then edgeCol is left and vice-versa. Values should come\n\t * from getMoveValues.\n\t * 1 = table name 2 = left 3 = right 4 = edgecol 5 = old.left 6 = old.right\n\t * 7 = target.pos (actually target.edgecol) 8 = dblist 9 = old.dblist\n\t */\n\tprivate String getSQLMoveItem(final String edgeCol, final Long edgeVal) {\n\t\tboolean movingLeft = Columns.LEFT.equals(edgeCol);\n\t\treturn String.format(\"UPDATE %1$s SET \" +\n\t\t\t\t\t\t// Left item follows Left = Left + ...\n\t\t\t\t\t\t\"%2$s = %2$s + \" +\n\t\t\t\t\t\t\" CASE \" +\n\t\t\t\t\t\t// Moving item jumps to target pos\n\t\t\t\t\t\t\" WHEN %2$s IS %5$d \" +\n\t\t\t\t\t\t// ex: left = 5, target = 2, --> left = 5 + (2 - 5) == 2\n\t\t\t\t\t\t// ex left = 5, target = 9(right), --> left = 5 + (9 - 5 - 1) = 8\n\t\t\t\t\t\t\" THEN \" +\n\t\t\t\t\t\t\" (%7$d - %5$d\" +\n\t\t\t\t\t\t(movingLeft ? \") \" : \" -1) \") +\n\t\t\t\t\t\t// Sub items take one step opposite\n\t\t\t\t\t\t// Careful if moving inside subtree, which can only\n\t\t\t\t\t\t// happen when moving right.\n\t\t\t\t\t\t// Then only left position changes\n\t\t\t\t\t\t\" WHEN %2$s BETWEEN (%5$d + 1) AND (%6$d - 1) \" +\n\t\t\t\t\t\t\" THEN \" +\n\t\t\t\t\t\t(movingLeft ? \" 1 \" : \" -1 \") +\n\t\t\t\t\t\t// Items in between from and to positions take two steps opposite\n\t\t\t\t\t\t\" WHEN %2$s BETWEEN \" +\n\t\t\t\t\t\t(movingLeft ? \"%7$d\" : \"%6$d\") +\n\t\t\t\t\t\t\" AND \" +\n\t\t\t\t\t\t(movingLeft ? \"%5$d\" : \"%7$d\") +\n\t\t\t\t\t\t\" THEN \" +\n\t\t\t\t\t\t(movingLeft ? \" 2 \" : \" -2 \") +\n\t\t\t\t\t\t// Not in target range, no change\n\t\t\t\t\t\t\" ELSE 0 END, \" +\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Right item follows Right = Right + ...\n\t\t\t\t\t\t */\n\t\t\t\t\t\t\" %3$s = %3$s + \" +\n\t\t\t\t\t\t\" CASE \" +\n\t\t\t\t\t\t// Moving item jumps to target pos\n\t\t\t\t\t\t\" WHEN %3$s IS %6$d \" +\n\t\t\t\t\t\t// ex: right = 7, target = 3(left), --> right = 7 + (3 - 7 + 1) == 4\n\t\t\t\t\t\t// ex right = 2, target = 9(right), --> right = 2 + (9 -  2) = 9\n\t\t\t\t\t\t\" THEN \" +\n\t\t\t\t\t\t\" (%7$d - %6$d\" +\n\t\t\t\t\t\t(movingLeft ? \" +1) \" : \") \") +\n\t\t\t\t\t\t// Sub items take one step opposite\n\t\t\t\t\t\t\" WHEN %3$s BETWEEN (%5$d + 1) AND (%6$d - 1) \" +\n\t\t\t\t\t\t\" THEN \" +\n\t\t\t\t\t\t(movingLeft ? \" 1 \" : \" -1 \") +\n\t\t\t\t\t\t// Items in between from and to positions take two steps opposite\n\t\t\t\t\t\t\" WHEN %3$s BETWEEN \" +\n\t\t\t\t\t\t(movingLeft ? \"%7$d\" : \"%6$d\") + \" AND \" +\n\t\t\t\t\t\t(movingLeft ? \"%5$d\" : \"%7$d\") + \" THEN \" +\n\t\t\t\t\t\t(movingLeft ? \" 2 \" : \" -2 \") +\n\t\t\t\t\t\t// Not in target range, no change\n\t\t\t\t\t\t\" ELSE 0 END \" +\n\t\t\t\t\t\t// And limit to the list in question\n\t\t\t\t\t\t\" WHERE %8$s IS %9$d;\", TABLE_NAME,\n\t\t\t\tColumns.LEFT, Columns.RIGHT, edgeCol, left, right, edgeVal, Columns.DBLIST, dblist);\n\t}\n\n\t/*\n\t * @SuppressLint(\"DefaultLocale\") public String getSQLMoveSubTree(final\n\t * ContentValues values) { return\n\t * String.format(\"UPDATE %1$s SET %2$s = %2$s + \" +\n\t *\n\t * \" CASE \" + // Tasks are moving left \" WHEN (%4$d < %6$d) \" +\n\t *\n\t * \" THEN CASE \" + \" WHEN %2$s BETWEEN %4$d AND (%6$d - 1) \" + // Then they\n\t * must flow [width] to the right \" THEN %7$d - %6$d + 1 \" +\n\t * \" WHEN %2$s BETWEEN %6$d AND %7$d \" + // Tasks in subtree jump to the\n\t * left // targetleft - left \" THEN %4$d - %6$d \" + // Do nothing otherwise\n\t * \" ELSE 0 END \" + // Tasks are moving right \" WHEN (%4$d > %6$d) \" +\n\t * \" THEN CASE \" + \" WHEN %2$s BETWEEN (%7$d + 1) AND %4$d \" + // Then move\n\t * them [width] to the left \" THEN %6$d - %7$d - 1\" +\n\t * \" WHEN %2$s BETWEEN %6$d AND %7$d \" + // Tasks in subtree jump to the\n\t * right // targetleft - left\n\t *\n\t * // Depends on if we are moving inside a task or // moving an entire one\n\t * \" THEN CASE WHEN %5$d > (%4$d + 1) \" + \" THEN %4$d - %7$d \" +\n\t * \" ELSE %4$d - %7$d + 1 END \" + // Do nothing otherwise \" ELSE 0 END \" +\n\t * // No move actually performed. comma to do right next \" ELSE 0 END, \" +\n\t *\n\t * \" %3$s = %3$s + \" + \" CASE \" + // Tasks are moving left\n\t * \" WHEN (%4$d < %6$d) \" +\n\t *\n\t * \" THEN CASE \" + // but only if right is left of originleft\n\t * \" WHEN %3$s BETWEEN %4$d AND (%6$d - 1)\" + // Then they must flow [width]\n\t * to the right \" THEN %7$d - %6$d + 1\" +\n\t * \" WHEN %2$s BETWEEN %6$d AND %7$d \" + // Tasks in subtree jump to the\n\t * left // targetleft - left \" THEN %4$d - %6$d \" + // Do nothing otherwise\n\t * \" ELSE 0 END \" + // Tasks are moving right \" WHEN (%4$d > %6$d) \" +\n\t * \" THEN CASE \" + // when right is between myright + 1 and targetleft + 1\n\t * \" WHEN %3$s BETWEEN (%7$d + 1) AND (%4$d + 1) \" + // Then move them\n\t * [width] to the left \" THEN %6$d - %7$d - 1\" +\n\t * \" WHEN %2$s BETWEEN %6$d AND %7$d \" + // targetleft - left // Depends on\n\t * if we are moving inside a task or // moving an entire one\n\t * \" THEN CASE WHEN %5$d > (%4$d + 1) \" + \" THEN %4$d - %7$d  \" +\n\t * \" ELSE %4$d - %7$d + 1 END \" + // Do nothing otherwise \" ELSE 0 END \" +\n\t * // No move actually performed. End update with semicolon \" ELSE 0 END \" +\n\t * \" WHERE %8$s IS %9$d; \"\n\t *\n\t * //Enforce integrity + posUniqueConstraint(\"new\",\n\t * \"pos not unique move sub tree\") ,\n\t *\n\t * TABLE_NAME, Columns.LEFT, Columns.RIGHT, values.getAsLong(TARGETLEFT),\n\t * values.getAsLong(TARGETRIGHT), left, right, Columns.DBLIST, dblist\n\t *\n\t * ); }\n\t */\n\n\t@Override\n\tpublic String getContentType() {\n\t\treturn CONTENT_TYPE;\n\t}\n\n\t/*\n\t * commodity functions added after 2022\n\t */\n\n\t/**\n\t * These headers are just indicative. If there is no task due in 3 days, \"tomorrow\" will\n\t * include all the current month, by design.\n\t *\n\t * @param input         The {@link String} received from the {@link Cursor} which, I think,\n\t *                      comes from the query on the view returned by\n\t *                      {@link #CREATE_SECTIONED_DATE_VIEW}\n\t * @param dueDateMillis the value of {@link Task.Columns#DUE} from the same {@link Cursor}\n\t *                      that gave you the \"input\" parameter\n\t * @return the name to show on a header of the {@link DragSortListView} when the notes are\n\t * ordered by date. For example, \"Tomorrow\", or \"Later\", depending on when the note\n\t * is due.\n\t */\n\tpublic static String getHeaderNameForListSortedByDate(String input, long dueDateMillis,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  @NonNull Context context) {\n\t\tString sTemp;\n\t\tif (Task.HEADER_KEY_OVERDUE.equals(input)) {\n\t\t\tsTemp = context.getString(R.string.date_header_overdue);\n\t\t} else if (Task.HEADER_KEY_TODAY.equals(input)) {\n\t\t\t// if you want to show text like \"next 15 days\" in the drag-sort-listview, you would\n\t\t\t// add your code in this class. For help, see where HEADER_KEY_PLUS4 is used\n\t\t\tsTemp = context.getString(R.string.date_header_today);\n\t\t} else if (Task.HEADER_KEY_PLUS1.equals(input)) {\n\t\t\tsTemp = context.getString(R.string.date_header_tomorrow);\n\t\t} else if (Task.HEADER_KEY_PLUS2.equals(input) || Task.HEADER_KEY_PLUS3.equals(input)\n\t\t\t\t|| Task.HEADER_KEY_PLUS4.equals(input)) {\n\t\t\t// Receives a \"Date\" and returns a string with the translated name of the week day\n\t\t\tSimpleDateFormat weekdayFormatter = TimeFormatter.getLocalFormatterWeekday(context);\n\t\t\tsTemp = weekdayFormatter.format(new Date(dueDateMillis));\n\t\t} else if (Task.HEADER_KEY_NEXT_MONTH.equals(input)) {\n\t\t\tsTemp = context.getString(R.string.next_month);\n\t\t} else if (Task.HEADER_KEY_NEXT_YEAR.equals(input)) {\n\t\t\tsTemp = context.getString(R.string.next_year);\n\t\t} else if (Task.HEADER_KEY_LATER.equals(input)) {\n\t\t\tsTemp = context.getString(R.string.date_header_future);\n\t\t} else if (Task.HEADER_KEY_NODATE.equals(input)) {\n\t\t\tsTemp = context.getString(R.string.date_header_none);\n\t\t} else if (Task.HEADER_KEY_COMPLETE.equals(input)) {\n\t\t\tsTemp = context.getString(R.string.date_header_completed);\n\t\t} else {\n\t\t\t// for compatibility with the old version, but i don't think it happens...\n\t\t\tsTemp = input;\n\t\t}\n\t\treturn sTemp;\n\t}\n\n\t/**\n\t * @return TRUE if this task was set as \"completed\" by the user, FALSE otherwise\n\t */\n\tpublic boolean isCompleted() {\n\t\treturn this.completed != null && this.completed > 0;\n\t}\n\n\t/**\n\t * @param id the {@link Columns#_ID} of the {@link Task} to return\n\t * @return the corresponding {@link Task} or NULL if it could not get one\n\t */\n\tpublic static Task byId(long id, Context context) {\n\t\tCursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(Task.URI, null, Task.Columns._ID + \" = ?\",\n\t\t\t\t\t\tnew String[] { Long.toString(id) }, null);\n\t\tif (c == null || c.getCount() != 1) return null;\n\t\tc.moveToFirst();\n\t\tvar x = new Task(c);\n\t\tc.close();\n\t\treturn x;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/TaskList.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\n\nimport com.nononsenseapps.notepad.R;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.Calendar;\n\npublic class TaskList extends DAO {\n\n\t// SQL convention says Table name should be \"singular\"\n\tpublic static final String TABLE_NAME = \"tasklist\";\n\tpublic static final Uri URI = Uri.withAppendedPath(\n\t\t\tUri.parse(MyContentProvider.SCHEME + MyContentProvider.AUTHORITY),\n\t\t\tTABLE_NAME);\n\n\tpublic static final String VIEWCOUNT_NAME = \"lists_with_count\";\n\tpublic static final Uri URI_WITH_COUNT = Uri.withAppendedPath(URI, VIEWCOUNT_NAME);\n\n\tpublic static Uri getUri(final long id) {\n\t\treturn Uri.withAppendedPath(URI, Long.toString(id));\n\t}\n\n\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.item/vnd.nononsenseapps.list\";\n\n\tpublic static final int BASEURICODE = 101;\n\tpublic static final int BASEITEMCODE = 102;\n\tpublic static final int VIEWCOUNTCODE = 103;\n\t// Legacy support, these also need to use legacy projections\n\tpublic static final int LEGACYBASEURICODE = 111;\n\tpublic static final int LEGACYBASEITEMCODE = 112;\n\tpublic static final int LEGACYVISIBLEURICODE = 113;\n\tpublic static final int LEGACYVISIBLEITEMCODE = 114;\n\n\t/**\n\t * TaskList URIs start at 101, up to 199\n\t */\n\tpublic static void addMatcherUris(UriMatcher sURIMatcher) {\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME, BASEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME + \"/#\", BASEITEMCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY, TABLE_NAME + \"/\"\n\t\t\t\t+ VIEWCOUNT_NAME, VIEWCOUNTCODE);\n\n\t\t// Legacy URIs\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Lists.LISTS, LEGACYBASEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Lists.LISTS + \"/#\", LEGACYBASEITEMCODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Lists.VISIBLE_LISTS,\n\t\t\t\tLEGACYVISIBLEURICODE);\n\t\tsURIMatcher.addURI(MyContentProvider.AUTHORITY,\n\t\t\t\tLegacyDBHelper.NotePad.Lists.VISIBLE_LISTS + \"/#\",\n\t\t\t\tLEGACYVISIBLEITEMCODE);\n\t}\n\n\tpublic static class Columns implements BaseColumns {\n\n\t\tprivate Columns() {}\n\n\t\tpublic static final String TITLE = \"title\";\n\t\tpublic static final String UPDATED = \"updated\";\n\t\tpublic static final String LISTTYPE = \"tasktype\";\n\t\tpublic static final String SORTING = \"sorting\";\n\n\t\tpublic static final String VIEW_COUNT = \"count\";\n\n\t\tpublic static final String[] FIELDS = { _ID, TITLE, UPDATED, LISTTYPE, SORTING };\n\t\t// GTASKACCOUNT, GTASKID };\n\t\tpublic static final String[] SHALLOWFIELDS = { _ID, TITLE, UPDATED };\n\t}\n\n\tpublic static final String CREATE_TABLE = \"CREATE TABLE \" +\n\t\t\tTABLE_NAME + \"(\" + Columns._ID +\n\t\t\t\" INTEGER PRIMARY KEY,\" + Columns.TITLE +\n\t\t\t\" TEXT NOT NULL DEFAULT '',\" + Columns.UPDATED +\n\t\t\t\" INTEGER,\" + Columns.LISTTYPE +\n\t\t\t\" TEXT DEFAULT NULL,\" + Columns.SORTING +\n\t\t\t\" TEXT DEFAULT NULL\" + \")\";\n\n\tpublic static final String CREATE_COUNT_VIEW = \"CREATE TEMP VIEW IF NOT EXISTS \" +\n\t\t\tVIEWCOUNT_NAME +\n\t\t\t\" AS SELECT \" +\n\t\t\tarrayToCommaString(Columns.FIELDS) +\n\t\t\t\",\" +\n\t\t\tColumns.VIEW_COUNT +\n\t\t\t\" FROM \" +\n\t\t\tTABLE_NAME +\n\t\t\t\" LEFT JOIN \" +\n\t\t\t// Select count statement\n\t\t\t\" (SELECT COUNT(1) AS \" + Columns.VIEW_COUNT +\n\t\t\t\",\" + Task.Columns.DBLIST + \" FROM \" +\n\t\t\tTask.TABLE_NAME + \" WHERE \" +\n\t\t\tTask.Columns.COMPLETED + \" IS NULL \" +\n\t\t\t\" GROUP BY \" + Task.Columns.DBLIST + \") \" +\n\t\t\t\" ON \" + TABLE_NAME + \".\" + Columns._ID +\n\t\t\t\" = \" + Task.Columns.DBLIST + \";\";\n\n\tpublic String title = \"\";\n\n\t// milliseconds since 1970-01-01 UTC\n\tpublic Long updated = null;\n\n\t/**\n\t * The database column is \"tasktype\". Either {@link R.string#const_listtype_notes},\n\t * {@link R.string#const_listtype_tasks} or NULL.\n\t * NULL means: use global preferences\n\t */\n\tpublic String listtype = null;\n\tpublic String sorting = null;\n\n\tpublic TaskList() {}\n\n\tpublic TaskList(final Cursor c) {\n\t\tthis._id = c.getLong(0);\n\t\tthis.title = c.getString(1);\n\t\tthis.updated = c.getLong(2);\n\t\tthis.listtype = c.getString(3);\n\t\tthis.sorting = c.getString(4);\n\t}\n\n\tpublic TaskList(final Uri uri, final ContentValues values) {\n\t\tthis(Long.parseLong(uri.getLastPathSegment()), values);\n\t}\n\n\tpublic TaskList(final long id, final ContentValues values) {\n\t\tthis(values);\n\t\tthis._id = id;\n\t}\n\n\tpublic TaskList(final JSONObject json) throws JSONException {\n\t\tif (json.has(Columns.TITLE))\n\t\t\ttitle = json.getString(Columns.TITLE);\n\t\tif (json.has(Columns.UPDATED))\n\t\t\tupdated = json.getLong(Columns.UPDATED);\n\t\tif (json.has(Columns.LISTTYPE))\n\t\t\tlisttype = json.getString(Columns.LISTTYPE);\n\t\tif (json.has(Columns.SORTING))\n\t\t\tsorting = json.getString(Columns.SORTING);\n\t}\n\n\tpublic TaskList(final ContentValues values) {\n\t\ttitle = values.getAsString(Columns.TITLE);\n\t\tupdated = values.getAsLong(Columns.UPDATED);\n\t\tlisttype = values.getAsString(Columns.LISTTYPE);\n\t\tsorting = values.getAsString(Columns.SORTING);\n\t}\n\n\tpublic ContentValues getContent() {\n\t\tfinal ContentValues values = new ContentValues();\n\t\t// Note that ID is NOT included here\n\t\tvalues.put(Columns.TITLE, title);\n\t\tvalues.put(Columns.UPDATED, updated);\n\t\tvalues.put(Columns.LISTTYPE, listtype);\n\t\tvalues.put(Columns.SORTING, sorting);\n\t\treturn values;\n\t}\n\n\t@Override\n\tprotected String getTableName() {\n\t\treturn TABLE_NAME;\n\t}\n\n\t@Override\n\tpublic String getContentType() {\n\t\treturn CONTENT_TYPE;\n\t}\n\n\t@Override\n\tpublic int save(final Context context) {\n\t\treturn save(context, Calendar.getInstance().getTimeInMillis());\n\t}\n\n\t/**\n\t * Insert or update this {@link TaskList} in the database\n\t */\n\tpublic int save(final Context context, final long updateTime) {\n\t\tint result = 0;\n\t\tupdated = updateTime;\n\t\tif (_id < 1) {\n\t\t\tfinal Uri uri = context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.insert(getBaseUri(), getContent());\n\t\t\tif (uri != null) {\n\t\t\t\t_id = Long.parseLong(uri.getLastPathSegment());\n\t\t\t\tresult++;\n\t\t\t}\n\t\t} else {\n\t\t\tresult += context\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.update(getUri(), getContent(), null, null);\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogConfirmBase.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.app.AlertDialog;\nimport android.app.Dialog;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.DialogFragment;\n\n/**\n * Simple confirm dialog fragment.\n */\npublic abstract class DialogConfirmBase extends DialogFragment {\n\n\tpublic interface DialogConfirmedListener {\n\t\tvoid onConfirm();\n\t}\n\n\tDialogConfirmedListener listener;\n\n\tpublic void setListener(final DialogConfirmedListener l) {\n\t\tlistener = l;\n\t}\n\n\tpublic DialogConfirmBase() {}\n\n\t@NonNull\n\t@Override\n\tpublic Dialog onCreateDialog(Bundle savedInstanceState) {\n\t\treturn new AlertDialog.Builder(getActivity())\n\t\t\t\t.setTitle(getTitle())\n\t\t\t\t.setMessage(getMessage())\n\t\t\t\t.setPositiveButton(android.R.string.ok, (dialog, which) -> onOKClick())\n\t\t\t\t.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())\n\t\t\t\t.create();\n\t}\n\n\tpublic abstract int getTitle();\n\n\tpublic abstract int getMessage();\n\n\tpublic abstract void onOKClick();\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogConfirmBaseV11.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.app.AlertDialog;\nimport android.app.Dialog;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.DialogFragment;\n\n/**\n * Simple confirm dialog fragment, extending from V11 fragment\n */\npublic abstract class DialogConfirmBaseV11 extends DialogFragment {\n\n\tpublic interface DialogConfirmedListener {\n\t\tvoid onConfirm();\n\t}\n\n\tDialogConfirmedListener listener;\n\n\tpublic void setListener(final DialogConfirmedListener l) {\n\t\tlistener = l;\n\t}\n\n\t@NonNull\n\t@Override\n\tpublic Dialog onCreateDialog(Bundle savedInstanceState) {\n\t\treturn new AlertDialog.Builder(getActivity())\n\t\t\t\t.setTitle(getTitle())\n\t\t\t\t.setMessage(getMessage())\n\t\t\t\t.setPositiveButton(android.R.string.ok, (dialog, which) -> onOKClick())\n\t\t\t\t.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())\n\t\t\t\t.create();\n\t}\n\n\tpublic abstract int getTitle();\n\n\tpublic abstract CharSequence getMessage();\n\n\tpublic abstract void onOKClick();\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteCompletedTasks.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.os.Bundle;\nimport android.widget.Toast;\n\nimport androidx.fragment.app.FragmentManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\n\n/**\n * Popup to confirm the user's choice to delete all completed tasks\n */\npublic class DialogDeleteCompletedTasks extends DialogDeleteTask {\n\n\tpublic static void showDialog(final FragmentManager fm, final long listId,\n\t\t\t\t\t\t\t\t  final DialogConfirmedListener listener) {\n\t\tDialogDeleteCompletedTasks d = new DialogDeleteCompletedTasks();\n\t\td.setListener(listener);\n\t\tBundle args = new Bundle();\n\t\targs.putLong(ID, listId);\n\t\td.setArguments(args);\n\t\td.show(fm, TAG);\n\t}\n\n\t@Override\n\tpublic int getMessage() {\n\t\treturn R.string.delete_completed_tasks_question;\n\t}\n\n\t@Override\n\tpublic void onOKClick() {\n\t\tString where = Task.Columns.COMPLETED + \" IS NOT NULL\";\n\t\tString[] whereArgs = null;\n\n\t\tswitch ((int) getArguments().getLong(ID, -1)) {\n\t\t\tcase TaskListFragment.LIST_ID_ALL:\n\t\t\t\t// Nothing to do. Take all completed\n\t\t\t\tbreak;\n\t\t\tcase TaskListFragment.LIST_ID_OVERDUE:\n\t\t\t\twhere += TaskListFragment.andWhereOverdue();\n\t\t\t\tbreak;\n\t\t\tcase TaskListFragment.LIST_ID_TODAY:\n\t\t\t\twhere += TaskListFragment.andWhereToday();\n\t\t\t\tbreak;\n\t\t\tcase TaskListFragment.LIST_ID_WEEK:\n\t\t\t\twhere += TaskListFragment.andWhereWeek();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\twhere += \" AND \" + Task.Columns.DBLIST + \" IS ?\";\n\t\t\t\twhereArgs = new String[] { Long\n\t\t\t\t\t\t.toString(getArguments().getLong(ID, -1)) };\n\t\t\t\tbreak;\n\t\t}\n\n\t\tint rowsDeleted = getActivity().getContentResolver().delete(Task.URI, where, whereArgs);\n\t\tif (0 < rowsDeleted) {\n\t\t\tToast.makeText(getActivity(), R.string.deleted, Toast.LENGTH_SHORT).show();\n\t\t}\n\t\tif (listener != null) {\n\t\t\tlistener.onConfirm();\n\t\t}\n\t\tgetDialog().dismiss();\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteList.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.os.Bundle;\nimport android.widget.Toast;\n\nimport androidx.fragment.app.FragmentManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.TaskList;\n\npublic class DialogDeleteList extends DialogConfirmBase {\n\n\tstatic final String ID = \"id\";\n\tstatic final String TAG = \"deletelistok\";\n\n\tpublic static void showDialog(final FragmentManager fm, final long listId,\n\t\t\t\t\t\t\t\t  final DialogConfirmedListener listener) {\n\t\tDialogDeleteList d = new DialogDeleteList();\n\t\td.setListener(listener);\n\t\tBundle args = new Bundle();\n\t\targs.putLong(ID, listId);\n\t\td.setArguments(args);\n\t\td.show(fm, TAG);\n\t}\n\n\t@Override\n\tpublic int getTitle() {\n\t\treturn R.string.delete_question;\n\t}\n\n\t@Override\n\tpublic int getMessage() {\n\t\treturn R.string.delete_list_message;\n\t}\n\n\t@Override\n\tpublic void onOKClick() {\n\t\tif (getArguments().getLong(ID, -1) > 0) {\n\t\t\tif (0 < getActivity().getContentResolver()\n\t\t\t\t\t.delete(TaskList.getUri(getArguments().getLong(ID, -1)),\n\t\t\t\t\t\t\tnull, null)) {\n\t\t\t\tToast.makeText(getActivity(), R.string.deleted, Toast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t}\n\t\tif (listener != null) {\n\t\t\tlistener.onConfirm();\n\t\t}\n\t\tgetDialog().dismiss();\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteTask.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.os.Bundle;\nimport android.widget.Toast;\n\nimport androidx.fragment.app.FragmentManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\n\npublic class DialogDeleteTask extends DialogConfirmBase {\n\n\tstatic final String ID = \"id\";\n\tstatic final String TAG = \"deletetaskok\";\n\n\tpublic static void showDialog(final FragmentManager fm, final long taskId,\n\t\t\t\t\t\t\t\t  final DialogConfirmedListener listener) {\n\t\tDialogDeleteTask d = new DialogDeleteTask();\n\t\td.setListener(listener);\n\t\tBundle args = new Bundle();\n\t\targs.putLong(ID, taskId);\n\t\td.setArguments(args);\n\t\td.show(fm, TAG);\n\t}\n\n\t@Override\n\tpublic int getTitle() {\n\t\treturn R.string.delete_question;\n\t}\n\n\t@Override\n\tpublic int getMessage() {\n\t\treturn R.string.delete_item_message;\n\t}\n\n\t@Override\n\tpublic void onOKClick() {\n\t\tif (getArguments().getLong(ID, -1) > 0) {\n\t\t\tint rowsDeleted = getActivity()\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.delete(Task.getUri(getArguments().getLong(ID, -1)),\n\t\t\t\t\t\t\tnull, null);\n\t\t\tif (0 < rowsDeleted) {\n\t\t\t\tToast.makeText(getActivity(), R.string.deleted, Toast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t}\n\t\tif (listener != null) {\n\t\t\tlistener.onConfirm();\n\t\t}\n\t\tgetDialog().dismiss();\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogEditList.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.UiThread;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.FragmentDialogEditlistBinding;\n\npublic class DialogEditList extends DialogFragment {\n\n\tpublic interface EditListDialogListener {\n\t\tvoid onFinishEditDialog(long id);\n\t}\n\n\tstatic final String LIST_ID = \"list_id\";\n\n\tprivate TaskList mTaskList;\n\n\tprivate EditListDialogListener listener;\n\n\t/**\n\t * for {@link R.layout#fragment_dialog_editlist}\n\t */\n\tprivate FragmentDialogEditlistBinding mBinding;\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tmBinding = FragmentDialogEditlistBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tsetup();\n\t\tmBinding.titleField.addTextChangedListener(new TextWatcher() {\n\t\t\t@Override\n\t\t\tpublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}\n\n\t\t\t@Override\n\t\t\tpublic void onTextChanged(CharSequence s, int start, int before, int count) {}\n\n\t\t\t@Override\n\t\t\tpublic void afterTextChanged(Editable s) {\n\t\t\t\t// allow to press OK if there is a title\n\t\t\t\tmBinding.buttons.dialogYes.setEnabled(mBinding.titleField.length() > 0);\n\t\t\t}\n\t\t});\n\t\tmBinding.deleteButton.setOnClickListener(v -> deleteClicked());\n\t\tmBinding.buttons.dialogNo.setOnClickListener(v -> dismiss());\n\t\tmBinding.buttons.dialogYes.setOnClickListener(v -> okClicked());\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\n\t/**\n\t * Use to create new list\n\t */\n\tpublic static DialogEditList getInstance() {\n\t\tDialogEditList dialog = new DialogEditList();\n\t\tdialog.setArguments(new Bundle());\n\t\treturn dialog;\n\t}\n\n\t/**\n\t * To edit an exising list\n\t */\n\tpublic static DialogEditList getInstance(final long listid) {\n\t\tDialogEditList dialog = new DialogEditList();\n\t\tBundle args = new Bundle();\n\t\targs.putLong(LIST_ID, listid);\n\t\tdialog.setArguments(args);\n\t\treturn dialog;\n\t}\n\n\tprotected DialogEditList() {}\n\n\tpublic void setListener(final EditListDialogListener listener) {\n\t\tthis.listener = listener;\n\t}\n\n\tvoid setup() {\n\t\t// New item hides delete button and disables OK initially\n\t\tif (getArguments().getLong(LIST_ID, -1) < 1) {\n\t\t\tmBinding.deleteButton.setVisibility(View.GONE);\n\t\t\tmBinding.buttons.dialogYes.setEnabled(false);\n\t\t}\n\n\t\tmBinding.modeSpinner.setAdapter(new ArrayAdapter<>(\n\t\t\t\tgetActivity(),\n\t\t\t\tR.layout.spinner_item,\n\t\t\t\tgetActivity().getResources().getStringArray(R.array.show_list_as)));\n\n\t\tmBinding.sortSpinner.setAdapter(new ArrayAdapter<>(\n\t\t\t\tgetActivity(),\n\t\t\t\tR.layout.spinner_item,\n\t\t\t\tgetActivity().getResources().getStringArray(R.array.sort_list_by)));\n\n\t\tif (getArguments().getLong(LIST_ID, -1) > 0) {\n\t\t\tgetDialog().setTitle(R.string.menu_managelists);\n\t\t\tLoaderManager.getInstance(this).restartLoader(0, null,\n\t\t\t\t\tnew LoaderCallbacks<Cursor>() {\n\n\t\t\t\t\t\t@NonNull\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {\n\t\t\t\t\t\t\treturn new CursorLoader(getActivity(),\n\t\t\t\t\t\t\t\t\tTaskList.getUri(getArguments().getLong(LIST_ID, -1)),\n\t\t\t\t\t\t\t\t\tTaskList.Columns.FIELDS, null, null,\n\t\t\t\t\t\t\t\t\tnull);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> arg0, Cursor c) {\n\t\t\t\t\t\t\tif (c.moveToFirst()) {\n\t\t\t\t\t\t\t\tmTaskList = new TaskList(c);\n\t\t\t\t\t\t\t\tDialogEditList.this.getActivity().runOnUiThread(() -> fillViews());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Don't need it anymore\n\t\t\t\t\t\t\tLoaderManager.getInstance(DialogEditList.this).destroyLoader(0);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> arg0) {}\n\t\t\t\t\t});\n\t\t} else {\n\t\t\tgetDialog().setTitle(R.string.menu_createlist);\n\t\t\tmTaskList = new TaskList();\n\t\t}\n\t}\n\n\t@UiThread\n\tvoid fillViews() {\n\t\tmBinding.titleField.setText(mTaskList.title);\n\t\tselectSortKey();\n\t\tselectListTypeKey();\n\n\t\t// Check if this is the default list\n\t\tfinal long defList = Long.parseLong(PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity())\n\t\t\t\t.getString(getString(R.string.pref_defaultlist), \"-1\"));\n\t\tif (mTaskList._id > 0 && defList == mTaskList._id) {\n\t\t\tmBinding.defaultListBox.setChecked(true);\n\t\t}\n\t}\n\n\n\tvoid deleteClicked() {\n\t\tif (mTaskList._id > 0) {\n\t\t\tDialogDeleteList.showDialog(getParentFragmentManager(), mTaskList._id, this::dismiss);\n\t\t}\n\t}\n\n\tvoid okClicked() {\n\t\tToast.makeText(getActivity(), R.string.saved, Toast.LENGTH_SHORT).show();\n\n\t\tmTaskList.title = mBinding.titleField.getText().toString();\n\t\tmTaskList.sorting = getSortValue();\n\t\tmTaskList.listtype = getListTypeValue();\n\n\t\tmTaskList.save(getActivity());\n\n\t\tif (mTaskList._id > 0 && mBinding.defaultListBox.isChecked()) {\n\t\t\tPreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t.edit()\n\t\t\t\t\t.putLong(getString(R.string.pref_defaultstartlist), mTaskList._id)\n\t\t\t\t\t.putString(getString(R.string.pref_defaultlist), Long.toString(mTaskList._id))\n\t\t\t\t\t.commit();\n\t\t} else if (mTaskList._id > 0) {\n\t\t\t// Remove pref if it is the default list currently\n\t\t\tfinal long defList = Long\n\t\t\t\t\t.parseLong(PreferenceManager\n\t\t\t\t\t\t\t.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t\t\t.getString(getString(R.string.pref_defaultlist), \"-1\"));\n\t\t\tfinal long defStartList = PreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t.getLong(getString(R.string.pref_defaultstartlist), -1);\n\t\t\tif (defList == mTaskList._id) {\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t\t.edit()\n\t\t\t\t\t\t.remove(getString(R.string.pref_defaultlist))\n\t\t\t\t\t\t.commit();\n\t\t\t}\n\t\t\tif (defStartList == mTaskList._id) {\n\t\t\t\tPreferenceManager.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t\t.edit()\n\t\t\t\t\t\t.remove(getString(R.string.pref_defaultstartlist))\n\t\t\t\t\t\t.commit();\n\t\t\t}\n\t\t}\n\n\t\tif (mTaskList._id > 0 && listener != null) {\n\t\t\tlistener.onFinishEditDialog(mTaskList._id);\n\t\t}\n\n\t\t// TODO save items if necessary\n\t\tthis.dismiss();\n\t}\n\n\tString getSortValue() {\n\t\t// Default from global prefs\n\t\tString result = null;\n\n\t\tfinal String key = (String) mBinding.sortSpinner.getSelectedItem();\n\t\tif (key.equals(getString(R.string.sort_list_alphabetical))) {\n\t\t\tresult = getString(R.string.const_alphabetic);\n\t\t} else if (key.equals(getString(R.string.sort_list_due))) {\n\t\t\tresult = getString(R.string.const_duedate);\n\t\t} else if (key.equals(getString(R.string.sort_list_manual))) {\n\t\t\tresult = getString(R.string.const_possubsort);\n\t\t} else if (key.equals(getString(R.string.sort_list_updated))) {\n\t\t\tresult = getString(R.string.const_modified);\n\t\t}\n\t\treturn result;\n\t}\n\n\tvoid selectSortKey() {\n\t\tif (mTaskList == null) return;\n\n\t\tif (mTaskList.sorting == null) {\n\t\t\tmBinding.sortSpinner.setSelection(0);\n\t\t} else if (mTaskList.sorting.equals(getString(R.string.const_alphabetic))) {\n\t\t\tmBinding.sortSpinner.setSelection(1);\n\t\t} else if (mTaskList.sorting.equals(getString(R.string.const_modified))) {\n\t\t\tmBinding.sortSpinner.setSelection(2);\n\t\t} else if (mTaskList.sorting.equals(getString(R.string.const_duedate))) {\n\t\t\tmBinding.sortSpinner.setSelection(3);\n\t\t} else if (mTaskList.sorting.equals(getString(R.string.const_possubsort))) {\n\t\t\tmBinding.sortSpinner.setSelection(4);\n\t\t}\n\t}\n\n\tString getListTypeValue() {\n\t\t// Default from global prefs\n\t\tString result = null;\n\n\t\tfinal String key = (String) mBinding.modeSpinner.getSelectedItem();\n\t\tif (key.equals(getString(R.string.show_items_as_notes))) {\n\t\t\tresult = getString(R.string.const_listtype_notes);\n\t\t} else if (key.equals(getString(R.string.show_items_as_tasks))) {\n\t\t\tresult = getString(R.string.const_listtype_tasks);\n\t\t}\n\t\treturn result;\n\t}\n\n\tvoid selectListTypeKey() {\n\t\tif (mTaskList == null) return;\n\n\t\tif (mTaskList.listtype == null) {\n\t\t\tmBinding.modeSpinner.setSelection(0);\n\t\t} else if (mTaskList.listtype.equals(getString(R.string.const_listtype_tasks))) {\n\t\t\tmBinding.modeSpinner.setSelection(1);\n\t\t} else if (mTaskList.listtype.equals(getString(R.string.const_listtype_notes))) {\n\t\t\tmBinding.modeSpinner.setSelection(2);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogExportBackup.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.net.Uri;\nimport android.os.Bundle;\n\nimport androidx.fragment.app.FragmentManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.prefs.BackupPrefs;\n\npublic class DialogExportBackup extends DialogConfirmBaseV11 {\n\tstatic final String ID = \"id\";\n\tstatic final String TAG = \"deletelistok\";\n\n\tpublic static void showDialog(final FragmentManager fm,\n\t\t\t\t\t\t\t\t  final DialogConfirmedListener listener) {\n\t\tDialogExportBackup d = new DialogExportBackup();\n\t\td.setListener(listener);\n\t\td.setArguments(new Bundle());\n\n\t\td.show(fm, TAG);\n\t}\n\n\t@Override\n\tpublic int getTitle() {\n\t\treturn R.string.backup_export;\n\t}\n\n\t@Override\n\tpublic CharSequence getMessage() {\n\t\tUri dir = BackupPrefs.getSelectedBackupDirUri(this.getContext());\n\t\tif (dir == null) return getString(R.string.unavailable_chose_directory);\n\n\t\t// ask users if they want to export to this folder\n\t\treturn getString(R.string.backup_export_msg, \"\\n\" + dir.getLastPathSegment());\n\t}\n\n\t@Override\n\tpublic void onOKClick() {\n\t\tif (listener != null) {\n\t\t\tlistener.onConfirm();\n\t\t}\n\t\tgetDialog().dismiss();\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogMoveToList.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.DAO;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.FragmentDialogMovetolistBinding;\n\nimport java.util.concurrent.Executors;\n\n/**\n * When you long-click a note, you can press a button on the actionbar to move it\n * to anoter list. Then, this popup shows up to let the user choose the destination\n */\npublic class DialogMoveToList extends DialogFragment {\n\n\tstatic final String TASK_IDS = \"task_ids\";\n\n\tprivate TaskList mTaskList;\n\n\tprivate long[] taskIds = null;\n\n\t/**\n\t * for {@link R.layout#fragment_dialog_movetolist}\n\t */\n\tprivate FragmentDialogMovetolistBinding mBinding;\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tmBinding = FragmentDialogMovetolistBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tsetup();\n\t\tmBinding.buttons.dialogNo.setOnClickListener(v -> dismiss());\n\t\tmBinding.buttons.dialogYes.setOnClickListener(v -> okClicked());\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\n\tpublic static DialogMoveToList getInstance(final Long... tasks) {\n\t\tlong[] taskIds = new long[tasks.length];\n\t\tfor (int i = 0; i < tasks.length; i++) {\n\t\t\ttaskIds[i] = tasks[i];\n\t\t}\n\n\t\treturn getInstance(taskIds);\n\t}\n\n\tpublic static DialogMoveToList getInstance(final long... taskIds) {\n\t\tDialogMoveToList dialog = new DialogMoveToList();\n\t\tBundle args = new Bundle();\n\t\targs.putLongArray(TASK_IDS, taskIds);\n\t\tdialog.setArguments(args);\n\t\treturn dialog;\n\t}\n\n\tpublic DialogMoveToList() {}\n\n\tvoid setup() {\n\t\tif (!getArguments().containsKey(TASK_IDS)) {\n\t\t\tdismiss();\n\t\t}\n\t\tthis.taskIds = getArguments().getLongArray(TASK_IDS);\n\n\t\tif (taskIds.length < 1) {\n\t\t\tdismiss();\n\t\t}\n\n\t\tgetDialog().setTitle(R.string.move_to);\n\n\t\t// Must select item first\n\t\tmBinding.buttons.dialogYes.setEnabled(false);\n\n\t\t// Adapter for list titles and ids\n\t\tfinal SimpleCursorAdapter adapter = new SimpleCursorAdapter(\n\t\t\t\tgetActivity(), R.layout.simple_light_list_item_activated_1,\n\t\t\t\tnull, new String[] { TaskList.Columns.TITLE },\n\t\t\t\tnew int[] { android.R.id.text1 }, 0);\n\t\t// Set it to the view\n\t\tmBinding.listView.setAdapter(adapter);\n\n\t\tmBinding.listView.setOnItemClickListener(\n\t\t\t\t(arg0, arg1, pos, id) -> mBinding.buttons.dialogYes.setEnabled(true));\n\n\t\t// Load content\n\t\tLoaderManager.getInstance(this).restartLoader(0, null,\n\t\t\t\tnew LoaderCallbacks<Cursor>() {\n\n\t\t\t\t\t@NonNull\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {\n\t\t\t\t\t\treturn new CursorLoader(getActivity(), TaskList.URI,\n\t\t\t\t\t\t\t\tTaskList.Columns.FIELDS, null, null,\n\t\t\t\t\t\t\t\tgetResources().getString(\n\t\t\t\t\t\t\t\t\t\tR.string.const_as_alphabetic,\n\t\t\t\t\t\t\t\t\t\tTaskList.Columns.TITLE));\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> arg0, Cursor c) {\n\t\t\t\t\t\tadapter.swapCursor(c);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> arg0) {\n\t\t\t\t\t\tadapter.swapCursor(null);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tvoid moveItems(final long toListId, final long[] taskIds) {\n\t\tExecutors.newSingleThreadExecutor().execute(() -> {\n\t\t\tfinal ContentValues val = new ContentValues();\n\t\t\tval.put(Task.Columns.DBLIST, toListId);\n\n\t\t\t// where _ID in (1, 2, 3)\n\t\t\tfinal String whereId = Task.Columns._ID + \" IN (\" + DAO.arrayToCommaString(taskIds) + \")\";\n\n\t\t\tgetActivity().getContentResolver().update(Task.URI, val, whereId, null);\n\t\t});\n\t}\n\n\tvoid okClicked() {\n\t\t// move items\n\t\tif (mBinding.listView.getCheckedItemPosition() == AdapterView.INVALID_POSITION) {\n\t\t\treturn;\n\t\t}\n\n\t\tfinal Cursor c = (Cursor) mBinding.listView\n\t\t\t\t.getItemAtPosition(mBinding.listView.getCheckedItemPosition());\n\t\tif (c != null) {\n\t\t\tfinal long targetListId = c.getLong(0);\n\t\t\tfinal String targetListTitle = c.getString(1);\n\n\t\t\tif (taskIds.length > 0 && targetListId > 0) {\n\t\t\t\tmoveItems(targetListId, taskIds);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tToast.makeText(getActivity(),\n\t\t\t\t\t\tgetString(R.string.moved_x_to_list, taskIds.length, targetListTitle),\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Guard against translations\n\t\t\t}\n\t\t}\n\n\t\tthis.dismiss();\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogPassword.java",
    "content": "/*\n * Copyright (C) 2012 Jonas Kalderstam\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.Animation;\nimport android.view.animation.AnimationUtils;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.databinding.FragmentDialogPasswordBinding;\nimport com.nononsenseapps.notepad.prefs.PasswordPrefs;\n\npublic class DialogPassword extends DialogFragment {\n\n\tPasswordConfirmedListener listener = null;\n\n\tpublic interface PasswordConfirmedListener {\n\t\tvoid onPasswordConfirmed();\n\t}\n\n\tpublic void setListener(final PasswordConfirmedListener listener) {\n\t\tthis.listener = listener;\n\t}\n\n\t/**\n\t * for {@link R.layout#fragment_dialog_password}\n\t */\n\tprivate FragmentDialogPasswordBinding mBinding;\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\t\tfinal String currentPassword = settings.getString(PasswordPrefs.KEY_PASSWORD, \"\");\n\t\tif (currentPassword.isEmpty()) {\n\t\t\tgetDialog().setTitle(R.string.enter_new_password);\n\t\t} else {\n\t\t\tgetDialog().setTitle(R.string.password_required);\n\t\t}\n\n\t\tmBinding = FragmentDialogPasswordBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tshowField();\n\t\tmBinding.buttons.dialogNo.setOnClickListener(v -> dismiss());\n\t\tmBinding.buttons.dialogYes.setOnClickListener(v -> confirm());\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\n\tpublic void showField() {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\t\tfinal String currentPassword = settings.getString(PasswordPrefs.KEY_PASSWORD, \"\");\n\t\tif (currentPassword.isEmpty()) {\n\t\t\tmBinding.passwordVerificationField.setVisibility(View.VISIBLE);\n\t\t} else {\n\t\t\tmBinding.passwordVerificationField.setVisibility(View.GONE);\n\t\t}\n\t}\n\n\tvoid confirm() {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\t\tfinal String currentPassword = settings.getString(PasswordPrefs.KEY_PASSWORD,\n\t\t\t\t\"\");\n\t\tfinal String enteredPassword = mBinding.passwordField.getText().toString();\n\t\tfinal String verifiedPassword = mBinding.passwordVerificationField.getText().toString();\n\n\t\tif (currentPassword.isEmpty()) {\n\t\t\tsetPassword(enteredPassword, verifiedPassword);\n\t\t} else {\n\t\t\t// We want to return true or false, user has entered correct password\n\t\t\tcheckPassword(enteredPassword, currentPassword);\n\t\t}\n\t}\n\n\tprivate void checkPassword(final String enteredPassword, final String currentPassword) {\n\t\tif (currentPassword.equals(enteredPassword)) {\n\t\t\tif (listener != null) {\n\t\t\t\tlistener.onPasswordConfirmed();\n\t\t\t}\n\t\t\tdismiss();\n\t\t} else {\n\t\t\tif (PreferencesHelper.areAnimationsEnabled(this.getContext())) {\n\t\t\t\t// shake the dialog to show that the password is wrong\n\t\t\t\tAnimation shake = AnimationUtils.loadAnimation(getActivity(), R.anim.shake);\n\t\t\t\tmBinding.passwordField.startAnimation(shake);\n\t\t\t}\n\t\t\tToast.makeText(getActivity(), getText(R.string.password_incorrect),\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t}\n\t}\n\n\tprivate void setPassword(final String pass1, final String pass2) {\n\t\tif (pass1 != null && !pass1.isEmpty() && pass1.equals(pass2)) {\n\t\t\tPreferenceManager.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t.edit()\n\t\t\t\t\t.putString(PasswordPrefs.KEY_PASSWORD, pass1)\n\t\t\t\t\t.commit();\n\t\t\tif (listener != null) {\n\t\t\t\tlistener.onPasswordConfirmed();\n\t\t\t}\n\t\t\tdismiss();\n\t\t} else {\n\t\t\tif (PreferencesHelper.areAnimationsEnabled(this.getContext())) {\n\t\t\t\t// shake the dialog to show that the password is wrong\n\t\t\t\tAnimation shake = AnimationUtils.loadAnimation(getActivity(), R.anim.shake);\n\t\t\t\tmBinding.passwordVerificationField.startAnimation(shake);\n\t\t\t}\n\t\t\tToast.makeText(getActivity(), getText(R.string.passwords_dont_match),\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogPasswordV11.java",
    "content": "/*\n * Copyright (C) 2012 Jonas Kalderstam\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.Animation;\nimport android.view.animation.AnimationUtils;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.databinding.FragmentDialogPasswordBinding;\nimport com.nononsenseapps.notepad.fragments.DialogPassword.PasswordConfirmedListener;\nimport com.nononsenseapps.notepad.prefs.PasswordPrefs;\n\n/**\n * Full copy of {@link DialogPassword}, but extending native fragment class\n * {@link android.app.DialogFragment} instead.\n * It is called when the user changes the existing password.\n * It asks to input the old password.\n */\npublic class DialogPasswordV11 extends DialogFragment {\n\n\t// TODO DialogPassword.java is better. Try to put the functions of this dialog back\n\t//  into that file, then delete this file\n\n\tPasswordConfirmedListener listener = null;\n\n\tpublic void setListener(final PasswordConfirmedListener listener) {\n\t\tthis.listener = listener;\n\t}\n\n\t/**\n\t * for {@link R.layout#fragment_dialog_password}\n\t */\n\tprivate FragmentDialogPasswordBinding mBinding;\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savInstState) {\n\t\tgetDialog().setTitle(R.string.password_required);\n\t\tmBinding = FragmentDialogPasswordBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tmBinding.buttons.dialogNo.setOnClickListener(v -> dismiss());\n\t\tmBinding.buttons.dialogYes.setOnClickListener(v -> confirm());\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\n\tvoid confirm() {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\t\tString currentPassword = settings.getString(PasswordPrefs.KEY_PASSWORD,\n\t\t\t\t\"\");\n\t\tString enteredPassword = mBinding.passwordField.getText().toString();\n\n\t\t// We want to return true or false, user has entered correct\n\t\t// password\n\t\tcheckPassword(enteredPassword, currentPassword);\n\t}\n\n\tprivate void checkPassword(String enteredPassword, String currentPassword) {\n\t\tif (\"\".equals(currentPassword) || currentPassword.equals(enteredPassword)) {\n\t\t\tif (listener != null) {\n\t\t\t\tlistener.onPasswordConfirmed();\n\t\t\t}\n\t\t\tdismiss();\n\t\t} else {\n\t\t\tif (PreferencesHelper.areAnimationsEnabled(this.getContext())) {\n\t\t\t\t// shake the dialog to show that the password is wrong\n\t\t\t\tAnimation shake = AnimationUtils.loadAnimation(getActivity(), R.anim.shake);\n\t\t\t\tmBinding.passwordField.startAnimation(shake);\n\t\t\t}\n\t\t\tToast.makeText(getActivity(), getText(R.string.password_incorrect),\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t}\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogRestore.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.FragmentDialogRestoreBinding;\n\npublic class DialogRestore extends DialogFragment {\n\n\tpublic interface OnListSelectedListener {\n\t\tvoid onListSelected(long listId);\n\t}\n\n\tprivate OnListSelectedListener listener;\n\n\t/**\n\t * for {@link R.layout#fragment_dialog_restore}\n\t */\n\tprivate FragmentDialogRestoreBinding mBinding;\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tmBinding = FragmentDialogRestoreBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tsetup();\n\t\tmBinding.buttons.dialogNo.setOnClickListener(v -> dismiss());\n\t\tmBinding.buttons.dialogYes.setOnClickListener(v -> okClicked());\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\n\t/**\n\t * Use to create new list\n\t */\n\tpublic static DialogRestore getInstance() {\n\t\tDialogRestore dialog = new DialogRestore();\n\t\tdialog.setArguments(new Bundle());\n\t\treturn dialog;\n\t}\n\n\tpublic DialogRestore() {}\n\n\tpublic void setListener(final OnListSelectedListener listener) {\n\t\tthis.listener = listener;\n\t}\n\n\n\tvoid setup() {\n\t\tgetDialog().setTitle(R.string.restore_to);\n\n\t\tfinal SimpleCursorAdapter adapter =\n\t\t\t\tnew SimpleCursorAdapter(getActivity(),\n\t\t\t\t\t\tR.layout.spinner_item, null,\n\t\t\t\t\t\tnew String[] { TaskList.Columns.TITLE },\n\t\t\t\t\t\tnew int[] { R.id.textViewSpinnerItem }, 0);\n\n\t\tmBinding.listSpinner.setAdapter(adapter);\n\n\t\tLoaderManager.getInstance(this).restartLoader(0, null,\n\t\t\t\tnew LoaderCallbacks<Cursor>() {\n\n\t\t\t\t\t@NonNull\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {\n\t\t\t\t\t\treturn new CursorLoader(getActivity(),\n\t\t\t\t\t\t\t\tTaskList.URI,\n\t\t\t\t\t\t\t\tTaskList.Columns.FIELDS,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tTaskList.Columns.TITLE);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> arg0, Cursor c) {\n\t\t\t\t\t\tadapter.swapCursor(c);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> arg0) {\n\t\t\t\t\t\tadapter.swapCursor(null);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tvoid okClicked() {\n\t\tToast.makeText(getActivity(), R.string.saved, Toast.LENGTH_SHORT).show();\n\n\t\t// TODO do something\n\t\tif (listener != null) {\n\t\t\tlistener.onListSelected(mBinding.listSpinner.getSelectedItemId());\n\t\t}\n\n\t\tthis.dismiss();\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogRestoreBackup.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\n\nimport android.net.Uri;\nimport android.os.Bundle;\n\nimport androidx.documentfile.provider.DocumentFile;\nimport androidx.fragment.app.FragmentManager;\n\nimport com.nononsenseapps.helpers.DocumentFileHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.prefs.BackupPrefs;\n\npublic class DialogRestoreBackup extends DialogConfirmBaseV11 {\n\tstatic final String ID = \"id\";\n\tstatic final String TAG = \"deletelistok\";\n\n\tpublic static void showDialog(final FragmentManager fm,\n\t\t\t\t\t\t\t\t  final DialogConfirmedListener listener) {\n\t\tDialogRestoreBackup d = new DialogRestoreBackup();\n\t\td.setListener(listener);\n\t\td.setArguments(new Bundle());\n\t\td.show(fm, TAG);\n\t}\n\n\t@Override\n\tpublic int getTitle() {\n\t\treturn R.string.backup_import;\n\t}\n\n\t@Override\n\tpublic CharSequence getMessage() {\n\t\tUri dir = BackupPrefs.getSelectedBackupDirUri(this.getContext());\n\t\tif (dir == null) return getString(R.string.unavailable_chose_directory);\n\n\t\tDocumentFile file = DocumentFileHelper.getSelectedBackupJsonFile(this.getContext());\n\t\tif (file == null) return getString(R.string.backup_file_not_found);\n\n\t\t// file found. Ask users if they want to import from it\n\t\treturn getString(R.string.backup_import_msg,\n\t\t\t\t\"\\n\" + file.getUri().getLastPathSegment());\n\t}\n\n\t@Override\n\tpublic void onOKClick() {\n\t\tif (listener != null) {\n\t\t\tlistener.onConfirm();\n\t\t}\n\t\tgetDialog().dismiss();\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/FragmentSearch.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.app.SearchManager;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView.OnItemClickListener;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter.ViewBinder;\nimport androidx.fragment.app.Fragment;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.databinding.FragmentSearchBinding;\nimport com.nononsenseapps.ui.TitleNoteTextView;\n\n/**\n * This is used only in the \"Archive\" view, for deleted notes.\n * For the search widget of the \"main\" view, see\n * {@link TaskListViewPagerFragment#onCreateOptionsMenu}\n */\npublic class FragmentSearch extends Fragment {\n\n\tpublic final static String QUERY = \"query\";\n\tprotected SimpleCursorAdapter mAdapter;\n\tprotected LoaderCallbacks<Cursor> mCallback;\n\tprotected String mQuery = \"\";\n\n\t/**\n\t * for {@link R.layout#fragment_search}\n\t */\n\tprotected FragmentSearchBinding mBinding;\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tmBinding = FragmentSearchBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tsetupAdapter();\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\n\tpublic static FragmentSearch getInstance(final String initialQuery) {\n\t\tFragmentSearch f = new FragmentSearch();\n\t\tBundle args = new Bundle();\n\t\targs.putString(QUERY, initialQuery);\n\t\tf.setArguments(args);\n\t\treturn f;\n\t}\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\tif (getArguments() != null) {\n\t\t\tif (getArguments().containsKey(QUERY))\n\t\t\t\tmQuery = getArguments().getString(QUERY);\n\t\t}\n\n\t\tsetHasOptionsMenu(true);\n\t}\n\n\t@Override\n\tpublic void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {\n\t\tsuper.onCreateOptionsMenu(menu, inflater);\n\n\t\t// allow the user to search among the previously deleted notes\n\t\tinflater.inflate(R.menu.fragment_search, menu);\n\n\t\t// Get the SearchView and set the searchable configuration\n\t\tSearchView searchView = (SearchView) menu\n\t\t\t\t.findItem(R.id.menu_search)\n\t\t\t\t.getActionView();\n\t\t// Assumes current activity is the searchable activity\n\t\tSearchManager sMan = this.getActivity().getSystemService(SearchManager.class);\n\t\tsearchView.setSearchableInfo(sMan.getSearchableInfo(getActivity().getComponentName()));\n\t\tsearchView.setIconifiedByDefault(false); // Do not iconify the widget;\n\t\t// expand it by default\n\t\tsearchView.setQueryRefinementEnabled(false);\n\t\tsearchView.setSubmitButtonEnabled(false);\n\n\t\t// Disable suggestions in \"note archive\" search activity\n\t\tsearchView.setSuggestionsAdapter(null);\n\n\t\tsearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {\n\t\t\t@Override\n\t\t\tpublic boolean onQueryTextSubmit(final String query) {\n\t\t\t\tdoSearch(query);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean onQueryTextChange(final String query) {\n\t\t\t\tdoSearch(query);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\n\t\tsearchView.setQuery(mQuery, false);\n\t}\n\n\tvoid setupAdapter() {\n\t\tmAdapter = getAdapter();\n\t\tmAdapter.setViewBinder(getViewBinder());\n\n\t\t// Set adapter\n\t\tmBinding.list.setAdapter(mAdapter);\n\t\tmBinding.list.setOnItemClickListener(getOnItemClickListener());\n\n\t\t// Start loading data\n\t\tmCallback = new LoaderCallbacks<>() {\n\t\t\t@NonNull\n\t\t\t@Override\n\t\t\tpublic Loader<Cursor> onCreateLoader(int id, Bundle arg1) {\n\t\t\t\treturn new CursorLoader(getActivity(), getSearchUri(), getFields(),\n\t\t\t\t\t\tnull, new String[] { mQuery }, getSortOrder());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor c) {\n\t\t\t\tmAdapter.swapCursor(c);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> loader) {\n\t\t\t\tmAdapter.swapCursor(null);\n\t\t\t}\n\t\t};\n\n\t\tdoSearch(mQuery);\n\t}\n\n\tprotected void doSearch(final String query) {\n\t\tmQuery = query == null ? \"\" : query;\n\n\t\t// If not loaded yet, let it load\n\t\tif (mCallback != null)\n\t\t\tLoaderManager.getInstance(this).restartLoader(0, null, mCallback);\n\t}\n\n\t/**\n\t * Override to give different search behaviour\n\t */\n\tprotected Uri getSearchUri() {\n\t\treturn Task.URI_SEARCH;\n\t}\n\n\t/**\n\t * Override to give different search behaviour\n\t */\n\tprotected String[] getFields() {\n\t\treturn Task.Columns.FIELDS;\n\t}\n\n\t/**\n\t * Override to give different search behaviour\n\t */\n\tprotected String getSortOrder() {\n\t\treturn Task.Columns.DUE;\n\t}\n\n\t/**\n\t * Override to get different search behaviour\n\t */\n\tprotected SimpleCursorAdapter getAdapter() {\n\t\treturn new SimpleCursorAdapter(\n\t\t\t\tgetActivity(),\n\t\t\t\tR.layout.tasklist_item_rich,\n\t\t\t\tnull,\n\t\t\t\tnew String[] { Task.Columns.TITLE, Task.Columns.NOTE, Task.Columns.DUE,\n\t\t\t\t\t\tTask.Columns.COMPLETED, Task.Columns.LEFT, Task.Columns.RIGHT },\n\t\t\t\t// in tasklist_idem_card_selection.xml, the due date is displayed in a DateView\n\t\t\t\t// with ID = \"date\", so here Task.Columns.DUE is bound to R.id.date\n\t\t\t\tnew int[] { android.R.id.text1, android.R.id.text1, R.id.date, R.id.checkbox,\n\t\t\t\t\t\tR.id.drag_handle, R.id.dragpadding },\n\t\t\t\t0);\n\t}\n\n\t/**\n\t * Override to give different search behaviour\n\t */\n\tprotected OnItemClickListener getOnItemClickListener() {\n\t\treturn (arg0, origin, pos, id)\n\t\t\t\t-> startActivity(new Intent(Intent.ACTION_EDIT, Task.getUri(id)));\n\t}\n\n\t/**\n\t * Override to give different search behaviour\n\t */\n\tprotected ViewBinder getViewBinder() {\n\t\t// Get the global list settings\n\t\tfinal SharedPreferences prefs = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\n\t\t// Load pref for item height, or show 3 lines if it was not set\n\t\tfinal int rowCount = prefs.getInt(getString(R.string.key_pref_item_max_height), 3);\n\n\t\treturn (view, c, colIndex) -> {\n\t\t\tswitch (colIndex) {\n\t\t\t\t// Matches order in Task.Columns.Fields\n\t\t\t\tcase 1 -> {\n\t\t\t\t\t// Title\n\t\t\t\t\tString sTemp = c.getString(1);\n\n\t\t\t\t\t// Set height of text for non-headers\n\t\t\t\t\tif (rowCount == 1) {\n\t\t\t\t\t\t((TitleNoteTextView) view).setSingleLine(true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t((TitleNoteTextView) view).setSingleLine(false);\n\t\t\t\t\t\t((TitleNoteTextView) view).setMaxLines(rowCount);\n\t\t\t\t\t}\n\t\t\t\t\t// Change color based on complete status\n\t\t\t\t\t((TitleNoteTextView) view).useSecondaryColor(!c.isNull(3));\n\t\t\t\t\t((TitleNoteTextView) view).setTextTitle(sTemp);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tcase 2 -> {\n\t\t\t\t\t// Note\n\t\t\t\t\t// Only if task it not locked\n\t\t\t\t\tif (c.getInt(9) != 1) {\n\t\t\t\t\t\t((TitleNoteTextView) view).setTextRest(c.getString(colIndex));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t((TitleNoteTextView) view).setTextRest(\"\");\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tcase 4 -> {\n\t\t\t\t\t// DateView\n\t\t\t\t\tif (!c.isNull(4)) {\n\t\t\t\t\t\t// there IS a due date saved in the database for this note\n\t\t\t\t\t\tlong dueDate = c.getLong(4);\n\t\t\t\t\t\t((com.nononsenseapps.ui.DateView) view).setTimeText(dueDate);\n\t\t\t\t\t\tview.setVisibility(View.VISIBLE);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// visibility of the DateView defaults to GONE\n\t\t\t\t\t\t// in tasklist_idem_card_selection.xml\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tdefault -> {\n\t\t\t\t\t// Checkbox, DragGripView, DragPadding; as defined in getAdapter() in this file\n\t\t\t\t\tview.setVisibility(View.GONE);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/FragmentSearchDeleted.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.content.SharedPreferences;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.view.ActionMode;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.AbsListView.MultiChoiceModeListener;\nimport android.widget.AdapterView.OnItemClickListener;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.UiThread;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter.ViewBinder;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.activities.ActivitySearchDeleted;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.DAO;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.ui.TitleNoteTextView;\n\nimport java.util.HashSet;\n\n/**\n * The actual content of the \"archive view\" in {@link ActivitySearchDeleted }\n */\npublic class FragmentSearchDeleted extends FragmentSearch {\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\tsuper.onViewCreated(view, savedInstanceState);\n\t\tsetSelection();\n\t}\n\n\tpublic static FragmentSearchDeleted getInstance(final String initialQuery) {\n\t\tFragmentSearchDeleted f = new FragmentSearchDeleted();\n\t\tBundle args = new Bundle();\n\t\targs.putString(QUERY, initialQuery);\n\t\tf.setArguments(args);\n\t\treturn f;\n\t}\n\n\tprivate FragmentSearchDeleted() {}\n\n\tvoid setSelection() {\n\t\tmBinding.list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);\n\t\tmBinding.list.setMultiChoiceModeListener(new MultiChoiceModeListener() {\n\n\t\t\tfinal HashSet<Long> selectedItems = new HashSet<>();\n\n\t\t\t@Override\n\t\t\tpublic boolean onPrepareActionMode(ActionMode mode, Menu menu) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onDestroyActionMode(ActionMode mode) {\n\t\t\t\tselectedItems.clear();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean onCreateActionMode(ActionMode mode, Menu menu) {\n\t\t\t\tfinal MenuInflater inflater = getActivity().getMenuInflater();\n\t\t\t\tinflater.inflate(R.menu.activity_deleted_context, menu);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tString[] getIdArray() {\n\t\t\t\tfinal String[] result = new String[selectedItems.size()];\n\t\t\t\tint i = 0;\n\t\t\t\tfor (final long id : selectedItems) {\n\t\t\t\t\tresult[i] = Long.toString(id);\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tvoid deleteSelected(final ActionMode mode) {\n\t\t\t\tString whereCondition = Task.Columns._ID + \" IN (\"\n\t\t\t\t\t\t+ DAO.arrayToCommaString(getIdArray()) + \")\";\n\t\t\t\tgetActivity()\n\t\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t\t.delete(Task.URI_DELETED_QUERY, whereCondition, null);\n\t\t\t\tselectedItems.clear();\n\t\t\t\t// mode.finish() touches the views, so it MUST run on the UI thread\n\t\t\t\tFragmentSearchDeleted.this.getActivity().runOnUiThread(mode::finish);\n\t\t\t}\n\n\t\t\tvoid restoreSelected(final ActionMode mode, final long listId) {\n\t\t\t\tfor (final Long id : selectedItems) {\n\t\t\t\t\tfinal int pos = getPosOfId(id);\n\t\t\t\t\tif (pos > -1) {\n\t\t\t\t\t\tfinal Cursor c = (Cursor) mBinding.list.getItemAtPosition(pos);\n\n\t\t\t\t\t\t// restore task\n\t\t\t\t\t\tfinal Task t = new Task();\n\t\t\t\t\t\tt.dblist = listId;\n\t\t\t\t\t\tt.title = c.getString(1);\n\t\t\t\t\t\tt.note = c.getString(2);\n\t\t\t\t\t\tt.completed = c.isNull(3) ? null : c.getLong(3);\n\t\t\t\t\t\tt.due = c.isNull(4) ? null : c.getLong(4);\n\t\t\t\t\t\tt.save(getActivity());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnotifySuccess();\n\t\t\t\tdeleteSelected(mode);\n\t\t\t}\n\n\t\t\tint getPosOfId(final long id) {\n\t\t\t\tint length = mBinding.list.getCount();\n\t\t\t\tint position;\n\t\t\t\tboolean found = false;\n\t\t\t\tfor (position = 0; position < length; position++) {\n\t\t\t\t\tif (id == mBinding.list.getItemIdAtPosition(position)) {\n\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!found) {\n\t\t\t\t\t// Happens both if list is empty\n\t\t\t\t\t// and if id is -1\n\t\t\t\t\tposition = -1;\n\t\t\t\t}\n\t\t\t\treturn position;\n\t\t\t}\n\n\t\t\t/** Show a {@link Toast} in a thread-safe way */\n\t\t\t@UiThread\n\t\t\tvoid notifySuccess() {\n\t\t\t\tnew Handler(Looper.getMainLooper()).post(() -> Toast.makeText(getActivity(),\n\t\t\t\t\t\tR.string.saved, Toast.LENGTH_SHORT).show());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {\n\t\t\t\tint itemId = item.getItemId();\n\t\t\t\tif (itemId == R.id.menu_restore) {\n\t\t\t\t\tDialogRestore d = DialogRestore.getInstance();\n\t\t\t\t\td.setListener(listId -> {\n\t\t\t\t\t\tif (listId > 0) {\n\t\t\t\t\t\t\trestoreSelected(mode, listId);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\td.show(getParentFragmentManager(), \"listselect\");\n\t\t\t\t\treturn true;\n\t\t\t\t} else if (itemId == R.id.menu_delete) {\n\t\t\t\t\tDialogDeleteTask.showDialog(getParentFragmentManager(), -1,\n\t\t\t\t\t\t\t() -> deleteSelected(mode));\n\t\t\t\t\treturn true;\n\t\t\t\t} else if (itemId == R.id.menu_select_all) {\n\t\t\t\t\t// Note: don't use .getChildCount(), that only reports the VISIBLE items,\n\t\t\t\t\t// so it's NEVER more than ~9, considering the average screen height!\n\t\t\t\t\tint howMany = mBinding.list.getAdapter().getCount();\n\n\t\t\t\t\tfor (int i = 0; i < howMany; i++) {\n\t\t\t\t\t\t// select every list item\n\t\t\t\t\t\tmBinding.list.setItemChecked(i, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onItemCheckedStateChanged(ActionMode mode, int position, long id,\n\t\t\t\t\t\t\t\t\t\t\t\t  boolean checked) {\n\t\t\t\tif (checked) {\n\t\t\t\t\tselectedItems.add(id);\n\t\t\t\t} else {\n\t\t\t\t\tselectedItems.remove(id);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tprotected Uri getSearchUri() {\n\t\treturn Task.URI_DELETED_QUERY;\n\t}\n\n\t@Override\n\tprotected String[] getFields() {\n\t\treturn Task.Columns.DELETEFIELDS;\n\t}\n\n\t@Override\n\tprotected String getSortOrder() {\n\t\treturn Task.Columns.TRIG_DELETED + \" DESC\";\n\t}\n\n\t@Override\n\tprotected OnItemClickListener getOnItemClickListener() {\n\t\treturn (arg0, origin, pos, id) -> mBinding.list.setItemChecked(pos, true);\n\t}\n\n\t@Override\n\tprotected SimpleCursorAdapter getAdapter() {\n\t\treturn new SimpleCursorAdapter(getActivity(),\n\t\t\t\tR.layout.tasklist_item_rich,\n\t\t\t\tnull,\n\t\t\t\tnew String[] { Task.Columns.TITLE, Task.Columns.NOTE, Task.Columns.DUE,\n\t\t\t\t\t\tTask.Columns.COMPLETED, Task.Columns.TRIG_DELETED,\n\t\t\t\t\t\tTask.Columns.TRIG_DELETED },\n\t\t\t\tnew int[] { android.R.id.text1, android.R.id.text1, R.id.date, R.id.checkbox,\n\t\t\t\t\t\tR.id.drag_handle, R.id.dragpadding },\n\t\t\t\t0);\n\t}\n\n\t@Override\n\tprotected ViewBinder getViewBinder() {\n\t\t// Get the global list settings\n\t\tfinal SharedPreferences prefs = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\n\t\t// Load preference for note height, or show 3 lines if it was not set\n\t\tfinal int rowCount = prefs.getInt(getString(R.string.key_pref_item_max_height), 3);\n\n\t\treturn (view, c, colIndex) -> {\n\t\t\tswitch (colIndex) {\n\t\t\t\t// the code here decides how the notes on the archive look like.\n\t\t\t\t// Each number in the \"case\" instruction matches the order in Task.Columns.Fields,\n\t\t\t\t// in fact c.getColumnNames() returns the fields of the note in the database:\n\t\t\t\t// [\"_id\", \"title\", \"note\", \"completed\", \"due\", \"dblist\", \"deletedtime\" ]\n\t\t\t\tcase 1 -> {\n\t\t\t\t\t// Title, in column \"title\"\n\t\t\t\t\tString noteTitle = c.getString(colIndex);\n\n\t\t\t\t\t// Set height of text for non-headers\n\t\t\t\t\tif (rowCount == 1) {\n\t\t\t\t\t\t((TitleNoteTextView) view).setSingleLine(true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t((TitleNoteTextView) view).setSingleLine(false);\n\t\t\t\t\t\t((TitleNoteTextView) view).setMaxLines(rowCount);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Change color based on complete status (column 3 is the \"completed\" status)\n\t\t\t\t\t((TitleNoteTextView) view).useSecondaryColor(!c.isNull(3));\n\n\t\t\t\t\t// TODO yes, completed note appear in dark gray in the archive view. I didn't\n\t\t\t\t\t//  know this. Make a TapTargetView to explain this to users. It could target\n\t\t\t\t\t//  the search icon, it doesn't matter. Just put it in onResume() or somewhere\n\t\t\t\t\t//  reasonable\n\n\t\t\t\t\t((TitleNoteTextView) view).setTextTitle(noteTitle);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tcase 2 -> {\n\t\t\t\t\t// Note content, in column \"note\". Let's show it even in the \"Archive\" view,\n\t\t\t\t\t// so that the user can distinguish 2 deleted notes with the same title\n\t\t\t\t\tString noteContent = c.getString(colIndex);\n\t\t\t\t\t((TitleNoteTextView) view).setTextRest(noteContent);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tcase 4 -> {\n\t\t\t\t\t// DateView\n\t\t\t\t\tif (!c.isNull(4)) {\n\t\t\t\t\t\t// there IS a due date saved in the database for this note\n\t\t\t\t\t\tlong dueDate = c.getLong(4);\n\t\t\t\t\t\t((com.nononsenseapps.ui.DateView) view).setTimeText(dueDate);\n\t\t\t\t\t\tview.setVisibility(View.VISIBLE);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// visibility of the DateView defaults to GONE\n\t\t\t\t\t\t// in tasklist_idem_card_selection.xml\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tdefault -> {\n\t\t\t\t\t// Checkbox, DragGripView, DragPadding; as defined in getAdapter() in this file\n\t\t\t\t\tview.setVisibility(View.GONE);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/TaskDetailFragment.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\n\nimport android.app.Activity;\nimport android.app.DatePickerDialog;\nimport android.app.TimePickerDialog;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.text.format.DateFormat;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.Button;\nimport android.widget.CheckBox;\nimport android.widget.DatePicker;\nimport android.widget.ImageButton;\nimport android.widget.LinearLayout;\nimport android.widget.ScrollView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.app.ShareCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.ListHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain;\nimport com.nononsenseapps.notepad.activities.ActivityTaskHistory;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.R.layout;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\nimport com.nononsenseapps.notepad.database.Notification;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.FragmentTaskDetailBinding;\nimport com.nononsenseapps.notepad.interfaces.MenuStateController;\nimport com.nononsenseapps.notepad.interfaces.OnFragmentInteractionListener;\nimport com.nononsenseapps.notepad.prefs.PrefsActivity;\nimport com.nononsenseapps.ui.NotificationItemHelper;\nimport com.nononsenseapps.ui.ShowcaseHelper;\nimport com.nononsenseapps.ui.StyledEditText;\n\nimport org.androidannotations.annotations.AfterViews;\nimport org.androidannotations.annotations.Click;\nimport org.androidannotations.annotations.EFragment;\nimport org.androidannotations.annotations.InstanceState;\nimport org.androidannotations.annotations.UiThread;\nimport org.androidannotations.annotations.UiThread.Propagation;\nimport org.androidannotations.annotations.ViewById;\n\nimport java.util.Calendar;\n\n/**\n * A fragment representing a single Note detail screen.\n * Lifecycle (order of the functions):\n * 1 - entering the fragment:\n * ...\n * 2 - exiting the fragment:\n * 2.1 - going back to {@link ActivityMain}, pressing \"+\" or \"undo\":\n * {@link #onPause()}\n * {@link #onStop()}\n * {@link #onDestroyView()}\n * {@link #onDestroy()}\n * {@link #onDetach()}\n * 2.2 - opening either {@link PrefsActivity} or {@link ActivityTaskHistory}:\n * {@link #onPause()}\n * {@link #onStop()}\n * 2.3 - share a note, opening the chooser panel\n * {@link #onPause()}\n * 2.3bis - then launch an activity to do something with the note being shared\n * {@link #onStop()}\n * 2.4 launch a popup, either \"delete\" or \"password lock\"\n * (none of those)\n */\n@EFragment\npublic class TaskDetailFragment extends Fragment {\n\n\tpublic static final int LOADER_EDITOR_TASK = 3001;\n\tpublic static final int LOADER_EDITOR_TASKLISTS = 3002;\n\tpublic static final int LOADER_EDITOR_NOTIFICATIONS = 3003;\n\n\tfinal LoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<>() {\n\t\t@Override\n\t\tpublic Loader<Cursor> onCreateLoader(final int id, final Bundle args) {\n\t\t\tif (LOADER_EDITOR_NOTIFICATIONS == id) {\n\t\t\t\treturn new CursorLoader(getActivity(), Notification.URI,\n\t\t\t\t\t\tNotification.Columns.FIELDS,\n\t\t\t\t\t\tNotification.Columns.TASKID + \" IS ?\",\n\t\t\t\t\t\tnew String[] { Long.toString(args.getLong(ARG_ITEM_ID,\n\t\t\t\t\t\t\t\t-1)) }, Notification.Columns.TIME);\n\t\t\t} else if (LOADER_EDITOR_TASK == id) {\n\t\t\t\treturn new CursorLoader(getActivity(), Task.getUri(args\n\t\t\t\t\t\t.getLong(ARG_ITEM_ID, -1)), Task.Columns.FIELDS, null,\n\t\t\t\t\t\tnull, null);\n\t\t\t} else if (LOADER_EDITOR_TASKLISTS == id) {\n\t\t\t\treturn new CursorLoader(getActivity(), TaskList.getUri(args\n\t\t\t\t\t\t.getLong(ARG_ITEM_LIST_ID)), TaskList.Columns.FIELDS,\n\t\t\t\t\t\tnull, null, null);\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onLoadFinished(Loader<Cursor> ldr, Cursor c) {\n\t\t\tif (LOADER_EDITOR_TASK == ldr.getId()) {\n\t\t\t\tif (c != null && c.moveToFirst()) {\n\t\t\t\t\tif (mTask == null) {\n\t\t\t\t\t\tmTask = new Task(c);\n\t\t\t\t\t\tif (mTaskOrg == null) {\n\t\t\t\t\t\t\tmTaskOrg = new Task(c);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfillUIFromTask();\n\t\t\t\t\t\t// Don't want updates while editing\n\t\t\t\t\t\t// getLoaderManager().destroyLoader(LOADER_EDITOR_TASK);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Don't want updates while editing\n\t\t\t\t\t\t// getLoaderManager().destroyLoader(LOADER_EDITOR_TASK);\n\t\t\t\t\t\t// Only update the list if that changes\n\t\t\t\t\t\tNnnLogger.debug(TaskDetailFragment.class,\n\t\t\t\t\t\t\t\t\"Updating list in task from \" + mTask.dblist);\n\t\t\t\t\t\tmTask.dblist = new Task(c).dblist;\n\t\t\t\t\t\tNnnLogger.debug(TaskDetailFragment.class,\n\t\t\t\t\t\t\t\t\"Updating list in task to \" + mTask.dblist);\n\t\t\t\t\t\tif (mTaskOrg != null) {\n\t\t\t\t\t\t\tmTaskOrg.dblist = mTask.dblist;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Load the list to see if we should hide task bits\n\t\t\t\t\tBundle args = new Bundle();\n\t\t\t\t\targs.putLong(ARG_ITEM_LIST_ID, mTask.dblist);\n\t\t\t\t\tLoaderManager\n\t\t\t\t\t\t\t.getInstance(TaskDetailFragment.this)\n\t\t\t\t\t\t\t.restartLoader(LOADER_EDITOR_TASKLISTS, args, this);\n\t\t\t\t\targs.clear();\n\t\t\t\t\targs.putLong(ARG_ITEM_ID, getArguments().getLong(ARG_ITEM_ID, stateId));\n\t\t\t\t\tLoaderManager\n\t\t\t\t\t\t\t.getInstance(TaskDetailFragment.this)\n\t\t\t\t\t\t\t.restartLoader(LOADER_EDITOR_NOTIFICATIONS, args, loaderCallbacks);\n\t\t\t\t} else {\n\t\t\t\t\t// Should kill myself maybe?\n\t\t\t\t}\n\t\t\t} else if (LOADER_EDITOR_NOTIFICATIONS == ldr.getId()) {\n\t\t\t\twhile (c != null && c.moveToNext()) {\n\t\t\t\t\t// TODO this causes the \"ghost reminder widgets\" bug. See issue #412\n\t\t\t\t\t//  a shitty fix is provided in onStop(). The problem is that it runs\n\t\t\t\t\t//  too many times, and it duplicates notification views. As of now it\n\t\t\t\t\t//  happens only when locking a note from the menu\n\t\t\t\t\taddNotification(new Notification(c));\n\t\t\t\t}\n\t\t\t\t// Don't update while editing\n\t\t\t\t// TODO this allows updating of the location name etc\n\t\t\t\tLoaderManager\n\t\t\t\t\t\t.getInstance(TaskDetailFragment.this)\n\t\t\t\t\t\t.destroyLoader(LOADER_EDITOR_NOTIFICATIONS);\n\t\t\t} else if (LOADER_EDITOR_TASKLISTS == ldr.getId()) {\n\t\t\t\t// At current only loading a single list\n\t\t\t\tif (c != null && c.moveToFirst()) {\n\t\t\t\t\tfinal TaskList list = new TaskList(c);\n\t\t\t\t\thideTaskParts(list);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> arg0) {}\n\t};\n\n\t@ViewById(resName = \"taskText\")\n\tStyledEditText taskText;\n\n\t@ViewById(resName = \"taskCompleted\")\n\tCheckBox taskCompleted;\n\n\t@ViewById(resName = \"dueDateBox\")\n\tButton dueDateBox;\n\n\t@ViewById(resName = \"dueCancelButton\")\n\tImageButton dueCancelButton;\n\n\t@ViewById(resName = \"notificationAdd\")\n\tTextView notificationAdd;\n\n\t/**\n\t * holds a list of widgets, one for each reminder the user sets.\n\t * It is below the \"due date\" row\n\t */\n\t@ViewById(resName = \"notificationList\")\n\tLinearLayout notificationList;\n\n\t@ViewById(resName = \"taskSection\")\n\tView taskSection;\n\n\t@ViewById(resName = \"editScrollView\")\n\tScrollView editScrollView;\n\n\t/**\n\t * Id of task to open\n\t */\n\tpublic static final String ARG_ITEM_ID = \"item_id\";\n\n\t/**\n\t * If no id is given, a string can be accepted as initial state\n\t */\n\tpublic static final String ARG_ITEM_CONTENT = \"item_text\";\n\n\t/**\n\t * A list id is necessary\n\t */\n\tpublic static final String ARG_ITEM_LIST_ID = \"item_list_id\";\n\n\t/**\n\t * preference key for the tutorial link message\n\t */\n\tprivate static final String SHOWCASED_EDITOR = \"showcased_tutorial_from_editor_window\";\n\n\t// To override intent values with\n\t@InstanceState\n\tlong stateId = -1;\n\n\t@InstanceState\n\tlong stateListId = -1;\n\n\t/**\n\t * Dao version of the object this fragment represents\n\t */\n\tprivate Task mTask;\n\n\t// Version when task was opened\n\tprivate Task mTaskOrg;\n\n\t/**\n\t * To save orgState\n\t */\n\t// TODO [use it in \"] AND [\" logic] with task.locked. If result is true, note is locked and\n\t//  has not been unlocked, otherwise good to show\n\tprivate boolean mLocked = true;\n\n\tprivate OnFragmentInteractionListener mListener;\n\n\t/**\n\t * If in tablet and added, rotating to portrait actually recreats the\n\t * fragment even though it isn't visible. So if this is true, don't load anything.\n\t */\n\tprivate boolean dontLoad = false;\n\n\t/**\n\t * Only calls other getter with the last segment parsed as long\n\t */\n\tpublic static TaskDetailFragment_ getInstance(final Uri itemUri) {\n\t\treturn getInstance(Long.parseLong(itemUri.getLastPathSegment()));\n\t}\n\n\t/**\n\t * Use to open an existing task\n\t */\n\tpublic static TaskDetailFragment_ getInstance(final long itemId) {\n\t\tBundle arguments = new Bundle();\n\t\targuments.putLong(ARG_ITEM_ID, itemId);\n\t\tTaskDetailFragment_ fragment = new TaskDetailFragment_();\n\t\tfragment.setArguments(arguments);\n\t\treturn fragment;\n\t}\n\n\t/**\n\t * Use to create a new task\n\t */\n\tpublic static TaskDetailFragment_ getInstance(String text, final long listId) {\n\t\tBundle arguments = new Bundle();\n\t\targuments.putString(ARG_ITEM_CONTENT, text);\n\t\targuments.putLong(ARG_ITEM_LIST_ID, listId);\n\t\tTaskDetailFragment_ fragment = new TaskDetailFragment_();\n\t\tfragment.setArguments(arguments);\n\t\treturn fragment;\n\t}\n\n\t/**\n\t * Mandatory empty constructor for the fragment manager to instantiate the\n\t * fragment (e.g. upon screen orientation changes).\n\t */\n\tpublic TaskDetailFragment() {\n\t\t// Make sure arguments are non-null\n\t\tif (getArguments() == null)\n\t\t\tsetArguments(new Bundle());\n\t}\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\t// restoreSavedInstanceState_(savedInstanceState);\n\t\tsuper.onCreate(savedInstanceState);\n\t}\n\n\t/**\n\t * for {@link R.layout#fragment_task_detail}\n\t */\n\tprivate FragmentTaskDetailBinding mBinding;\n\n\t/* @Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tif (container == null) {\n\t\t\tdontLoad = true;\n\t\t\treturn null;\n\t\t}\n\t\tsetHasOptionsMenu(true);\n\t\tmBinding = FragmentTaskDetailBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tsetListeners();\n\t\tmBinding.dueDateBox.setOnClickListener(v -> onDateClick());\n\t\tmBinding.notificationAdd.setOnClickListener(v -> onAddReminder());\n\t\tmBinding.dueCancelButton.setOnClickListener(v -> onDueRemoveClick());\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\t*/\n\n\t/**\n\t * Must handle this manually because annotations do not return null if\n\t * container is null\n\t */\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,\n\t\t\t\t\t\t\t Bundle savInstState) {\n\t\tif (container == null) {\n\t\t\tdontLoad = true;\n\t\t\treturn null;\n\t\t}\n\t\tsetHasOptionsMenu(true); // needed, to have the actionbar menu (+, share, ...)\n\t\treturn inflater.inflate(layout.fragment_task_detail, container, false);\n\t}\n\n\t@Override\n\tpublic void onActivityCreated(final Bundle state) {\n\t\tsuper.onActivityCreated(state);\n\n\t\tif (dontLoad) {\n\t\t\treturn;\n\t\t}\n\n\t\tboolean shouldOpenKeyBoard = false;\n\t\tfinal Bundle args = new Bundle();\n\t\tlong argItemId = getArguments().getLong(ARG_ITEM_ID, stateId);\n\t\tlong argItemListId = getArguments().getLong(ARG_ITEM_LIST_ID, stateListId);\n\n\t\tif (argItemId > 0) {\n\t\t\t// existing note => Load data from database\n\t\t\targs.putLong(ARG_ITEM_ID, argItemId);\n\t\t\tLoaderManager\n\t\t\t\t\t.getInstance(this)\n\t\t\t\t\t.restartLoader(LOADER_EDITOR_TASK, args, loaderCallbacks);\n\t\t} else {\n\t\t\t// new note => create it\n\n\t\t\t// If the given list is not valid, find a valid list\n\t\t\tif (argItemListId < 1) {\n\t\t\t\tgetArguments().putLong(ARG_ITEM_LIST_ID,\n\t\t\t\t\t\tListHelper.getARealList(getActivity(), -1));\n\t\t\t\t// then update the variable\n\t\t\t\targItemListId = getArguments().getLong(ARG_ITEM_LIST_ID, stateListId);\n\t\t\t}\n\t\t\t// Fail if the list is still not valid\n\t\t\tif (argItemListId < 1) {\n\t\t\t\t// simulate an exception\n\t\t\t\tToast.makeText(getActivity(), \"Must specify a list id to create a note in!\",\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\tNnnLogger.error(TaskDetailFragment.class, \"argItemListId < 1\");\n\t\t\t\tgetActivity().finish();\n\t\t\t}\n\t\t\targs.putLong(ARG_ITEM_LIST_ID, argItemListId);\n\t\t\tLoaderManager\n\t\t\t\t\t.getInstance(this)\n\t\t\t\t\t.restartLoader(LOADER_EDITOR_TASKLISTS, args, loaderCallbacks);\n\n\t\t\tshouldOpenKeyBoard = true;\n\t\t\tmTaskOrg = new Task();\n\t\t\tmTask = new Task();\n\t\t\tmTask.dblist = getArguments().getLong(ARG_ITEM_LIST_ID);\n\t\t\t// New note but start with the text given\n\t\t\tmTask.setText(getArguments().getString(ARG_ITEM_CONTENT, \"\"));\n\t\t\tfillUIFromTask();\n\t\t}\n\n\t\t// showcase first time\n\t\tfinal boolean showcasing = showcaseEditor();\n\n\t\tif (!showcasing && shouldOpenKeyBoard) {\n\t\t\t// Only show keyboard for new/empty notes,\n\t\t\t// but not if the showcaseview is showing\n\t\t\ttaskText.requestFocus();\n\t\t\tInputMethodManager imm = getContext().getSystemService(InputMethodManager.class);\n\t\t\timm.showSoftInput(taskText, InputMethodManager.SHOW_IMPLICIT);\n\t\t}\n\t}\n\n\t/**\n\t * Show the message to tell the user about our online tutorial\n\t *\n\t * @return true if showcase window is visible\n\t */\n\tboolean showcaseEditor() {\n\t\tfinal boolean alreadyShowcased = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity())\n\t\t\t\t.getBoolean(SHOWCASED_EDITOR, false);\n\n\t\tif (alreadyShowcased) return false;\n\n\t\tShowcaseHelper.showForOverflowMenu(this.getActivity(),\n\t\t\t\tR.string.showcase_tutorial_title,\n\t\t\t\tR.string.showcase_tutorial_description);\n\n\t\tPreferenceManager.getDefaultSharedPreferences(getActivity())\n\t\t\t\t.edit()\n\t\t\t\t.putBoolean(SHOWCASED_EDITOR, true)\n\t\t\t\t.commit();\n\t\treturn true;\n\t}\n\n\t@AfterViews\n\tvoid setListeners() {\n\t\tif (dontLoad) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set chosen attributes on the text field\n\t\tfinal SharedPreferences prefs = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\n\t\ttaskText.setTitleRelativeLarger(prefs.getBoolean(\n\t\t\t\tgetString(R.string.pref_editor_biggertitles), true));\n\t\ttaskText.setTitleFontFamily(Integer.parseInt(prefs.getString(\n\t\t\t\tgetString(R.string.pref_editor_title_fontfamily), \"2\")));\n\t\ttaskText.setTitleFontStyle(Integer.parseInt(prefs.getString(\n\t\t\t\tgetString(R.string.pref_editor_title_fontstyle), \"0\")));\n\t\ttaskText.setBodyFontFamily(Integer.parseInt(prefs.getString(\n\t\t\t\tgetString(R.string.pref_editor_body_fontfamily), \"0\")));\n\t\ttaskText.setLinkify(prefs.getBoolean(\n\t\t\t\tgetString(R.string.pref_editor_links), true));\n\t\ttaskText.setTheTextSize(Integer.parseInt(prefs.getString(\n\t\t\t\tgetString(R.string.pref_editor_fontsize), \"1\")));\n\t}\n\n\t@Click(resName = \"dueDateBox\")\n\tvoid onDateClick() {\n\t\tfinal Calendar localTime = Calendar.getInstance();\n\t\tif (mTask != null && mTask.due != null) {\n\t\t\t//datePicker = DialogCalendar.getInstance(mTask.due);\n\t\t\tlocalTime.setTimeInMillis(mTask.due);\n\t\t}// else {\n\t\t//\tdatePicker = DialogCalendar.getInstance();\n\t\t//}\n\n\t\t//final DialogCalendar datePicker;\n\t\t//datePicker.setListener(this);\n\t\t//datePicker.show(getParentFragmentManager(), DATE_DIALOG_TAG);\n\n\t\t// configure and show a popup with a date-picker calendar view\n\t\tvar dpDiag = new DatePickerDialog(\n\t\t\t\tthis.getActivity(),\n\t\t\t\t// ThemeHelper.getPickerDialogTheme(this.getContext()),\n\t\t\t\tthis::onDateSet,\n\t\t\t\tlocalTime.get(Calendar.YEAR),\n\t\t\t\tlocalTime.get(Calendar.MONTH),\n\t\t\t\tlocalTime.get(Calendar.DAY_OF_MONTH));\n\t\tdpDiag.setTitle(R.string.select_date);\n\t\tdpDiag.show();\n\t}\n\n\tprivate void onDateSet(DatePicker dialog, int year, int monthOfYear, int dayOfMonth) {\n\t\tfinal Calendar localTime = Calendar.getInstance();\n\t\tif (mTask.due != null) {\n\t\t\tlocalTime.setTimeInMillis(mTask.due);\n\t\t}\n\t\tlocalTime.set(Calendar.YEAR, year);\n\t\tlocalTime.set(Calendar.MONTH, monthOfYear);\n\t\tlocalTime.set(Calendar.DAY_OF_MONTH, dayOfMonth);\n\n\t\t// set to 23:59 to be more or less consistent with earlier date only implementation\n\t\tlocalTime.set(Calendar.HOUR_OF_DAY, 23);\n\t\tlocalTime.set(Calendar.MINUTE, 59);\n\n\t\tmTask.due = localTime.getTimeInMillis();\n\t\tsetDueText();\n\n\t\t/* TODO if you want the user to set a due time (we only set the due date for now)\n\t\t    then simply uncomment this code (and code referenced in other TODOs like this)\n\t\t// then ask for due time\n\t\tgetTimePickerDialog(localTime, (theWidget, hourOfDay, minute) -> {\n\t\t\tlocalTime.set(Calendar.HOUR_OF_DAY, hourOfDay);\n\t\t\tlocalTime.set(Calendar.MINUTE, minute);\n\t\t\tmTask.due = localTime.getTimeInMillis();\n\t\t\tsetDueText();\n\t\t}).show();\n\t\t*/\n\t}\n\n\tprivate void setDueText() {\n\t\tif (mTask.due == null) {\n\t\t\tdueDateBox.setText(\"\");\n\t\t} else {\n\t\t\t// Due date\n\t\t\tdueDateBox.setText(TimeFormatter.getLocalDateOnlyStringLong(getActivity(), mTask.due));\n\t\t\t// TODO if you want to let the user set a \"due time\" (as of now we have only\n\t\t\t//  the due date) replace the function above with TimeFormatter.getLocalDateStringLong()\n\t\t}\n\t}\n\n\t@Click(resName = \"dueCancelButton\")\n\tvoid onDueRemoveClick() {\n\t\tif (!isLocked()) {\n\t\t\tif (mTask != null) {\n\t\t\t\tmTask.due = null;\n\t\t\t}\n\t\t\tsetDueText();\n\t\t}\n\t}\n\n\t@Click(resName = \"notificationAdd\")\n\tvoid onAddReminder() {\n\t\tif (mTask != null && !isLocked()) {\n\t\t\t// IF no id, have to save first\n\t\t\tif (mTask._id < 1) {\n\t\t\t\tsaveTask();\n\t\t\t}\n\t\t\t// Only allow if save succeeded\n\t\t\tif (mTask._id < 1) {\n\t\t\t\tToast.makeText(getActivity(),\n\t\t\t\t\t\tR.string.please_type_before_reminder,\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfinal Notification not = new Notification(mTask._id);\n\t\t\tnot.save(getActivity(), true);\n\n\t\t\t// add item to UI\n\t\t\taddNotification(not);\n\n\t\t\t// And scroll to bottom. takes 300ms for item to appear.\n\t\t\teditScrollView.postDelayed(\n\t\t\t\t\t() -> editScrollView.fullScroll(ScrollView.FOCUS_DOWN),\n\t\t\t\t\t300);\n\t\t}\n\t}\n\n\t/**\n\t * \"not having a password in the app\" is different from\n\t * \"the note being saved as un/locked in the database\", so this function does not check for a\n\t * password. If the note is locked and a password is not set, a popup will launch and ask\n\t * the user to set a new, app-wide password.\n\t *\n\t * @return TRUE if {@link #mTask} is password-protected, by evaluating \"task.locked & mLocked\"\n\t */\n\tpublic boolean isLocked() {\n\t\tif (mTask != null) {\n\t\t\treturn mTask.locked & mLocked;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @implNote this method MUST be annotated with {@link UiThread.Propagation#REUSE}\n\t * and not simply {@link UiThread}, to ensure that this runs before\n\t * {@link #onPause()} when called from {@link #onActivityCreated}. This avoids\n\t * a bug that deletes the note when receiving shared text (a link from google\n\t * chrome, for example). The bug could be seen in API 23 (LineageOS 13) with\n\t * NoNonsenseNotes 7.1.0, for example.\n\t */\n\t@UiThread(propagation = Propagation.REUSE)\n\tvoid fillUIFromTask() {\n\t\tif (taskText == null || taskCompleted == null) {\n\t\t\t// it gets triggered ONLY in espresso tests!\n\t\t\tNnnLogger.error(TaskDetailFragment.class, \"taskText or taskCompleted is null\");\n\t\t\treturn;\n\t\t}\n\t\tNnnLogger.debug(TaskDetailFragment.class, \"fillUI, activity: \" + getActivity());\n\t\tif (isLocked()) {\n\t\t\ttaskText.setText(mTask.title);\n\t\t\tDialogPassword pflock = new DialogPassword();\n\t\t\tpflock.setListener(() -> {\n\t\t\t\tmLocked = false;\n\t\t\t\tfillUIFromTask();\n\t\t\t});\n\t\t\t// show the password popup if needed\n\t\t\tfinal String PASSW_TAG = \"read_verify\";\n\t\t\tFragment oldDiag = this.getParentFragmentManager().findFragmentByTag(PASSW_TAG);\n\t\t\tif (oldDiag == null) {\n\t\t\t\t// the password dialog is not among the active fragments\n\t\t\t\t// => you have to launch it\n\t\t\t\tpflock.show(getParentFragmentManager(), PASSW_TAG);\n\t\t\t} else {\n\t\t\t\t// there is already one visible => don't spam them, one is enough\n\t\t\t}\n\t\t} else {\n\t\t\ttaskText.setText(mTask.getText());\n\t\t}\n\t\tsetDueText();\n\t\ttaskCompleted.setChecked(mTask.completed != null);\n\t\ttaskCompleted.setOnCheckedChangeListener((buttonView, isChecked) -> {\n\t\t\tif (isChecked)\n\t\t\t\tmTask.completed = Calendar.getInstance().getTimeInMillis();\n\t\t\telse\n\t\t\t\tmTask.completed = null;\n\t\t});\n\n\t\t// Lock fields\n\t\tsetFieldStatus();\n\t}\n\n\t/**\n\t * Set fields to enabled/disabled depending on wether the note is locked\n\t */\n\tvoid setFieldStatus() {\n\t\tfinal boolean status = !isLocked();\n\t\ttaskText.setEnabled(status);\n\t\ttaskCompleted.setEnabled(status);\n\t\tdueDateBox.setEnabled(status);\n\t\tdueCancelButton.setEnabled(status);\n\t\tnotificationAdd.setEnabled(status);\n\t\tnotificationList.setEnabled(status);\n\n\t\t// by default it does not gray out the icons, and it's tricky to do it in code.\n\t\t// It does not matter because we block the click callbacks\n\t\t// in NotificationItemHelper.setup()\n\t}\n\n\tvoid hideTaskParts(final TaskList list) {\n\t\tString type;\n\t\tif (list.listtype == null) {\n\t\t\ttype = PreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t.getString(getString(R.string.pref_listtype), getString(R.string.default_listtype));\n\t\t} else {\n\t\t\ttype = list.listtype;\n\t\t}\n\t\ttaskSection.setVisibility(\n\t\t\t\ttype.equals(getString(R.string.const_listtype_notes)) ? View.GONE : View.VISIBLE);\n\t}\n\n\t@Override\n\tpublic void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {\n\t\tinflater.inflate(R.menu.fragment_tasks_detail, menu);\n\t\tsuper.onCreateOptionsMenu(menu, inflater);\n\t}\n\n\t/**\n\t * @return an {@link Intent} that creates a panel to choose an app,\n\t * to share the note being edited in this {@link TaskDetailFragment},\n\t * or NULL if the note was not in a valid state\n\t */\n\t@Nullable\n\tprivate Intent getShareIntent() {\n\t\tif (taskText == null || taskText.getText() == null) return null;\n\t\tString text = taskText.getText().toString();\n\t\tint titleEnd = text.indexOf(\"\\n\");\n\t\tif (titleEnd < 0) {\n\t\t\ttitleEnd = text.length();\n\t\t}\n\t\tString noteTitle = text.substring(0, titleEnd);\n\n\t\treturn new ShareCompat\n\t\t\t\t.IntentBuilder(this.getContext())\n\t\t\t\t.setType(\"text/plain\")\n\t\t\t\t.setText(text)\n\t\t\t\t.setSubject(noteTitle) // for email apps\n\t\t\t\t.setChooserTitle(noteTitle) // for the chooser panel\n\t\t\t\t.createChooserIntent();\n\t}\n\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tint itemId = item.getItemId();\n\t\tif (itemId == R.id.menu_add) {\n\t\t\t// TODO should not call if in tablet mode\n\t\t\tif (mListener != null && mTask != null && mTask.dblist > 0) {\n\t\t\t\tmListener.addTaskInList(\"\", mTask.dblist);\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_revert) {\n\t\t\t// set to null to prevent modifications\n\t\t\tmTask = null;\n\t\t\t// Request a close from activity\n\t\t\tif (mListener != null) {\n\t\t\t\tmListener.closeFragment(this);\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_timemachine) {\n\t\t\tif (mTask != null && mTask._id > 0) {\n\t\t\t\tIntent timeIntent = new Intent(getActivity(), ActivityTaskHistory.class);\n\t\t\t\ttimeIntent.putExtra(Task.Columns._ID, mTask._id);\n\t\t\t\tstartActivityForResult(timeIntent, 1);\n\t\t\t\t// ActivityTaskHistory.start(getActivity(), mTask._id);\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_delete) {\n\t\t\tif (mTask != null) {\n\t\t\t\tif (mTask.locked) {\n\t\t\t\t\tDialogPassword delpf = new DialogPassword();\n\t\t\t\t\tdelpf.setListener(this::deleteAndClose);\n\t\t\t\t\tdelpf.show(getParentFragmentManager(), \"delete_verify\");\n\t\t\t\t} else {\n\t\t\t\t\tdeleteAndClose();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_lock) {\n\t\t\tDialogPassword pflock = new DialogPassword();\n\t\t\tpflock.setListener(() -> {\n\t\t\t\tif (mTask != null) {\n\t\t\t\t\tmLocked = true;\n\t\t\t\t\tmTask.locked = true;\n\t\t\t\t\tmTask.save(getActivity());\n\t\t\t\t\tfillUIFromTask();\n\t\t\t\t\tToast.makeText(getActivity(), R.string.locked, Toast.LENGTH_SHORT).show();\n\t\t\t\t}\n\t\t\t});\n\t\t\tpflock.show(getParentFragmentManager(), \"lock_verify\");\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_unlock) {\n\t\t\tDialogPassword pf = new DialogPassword();\n\t\t\tpf.setListener(() -> {\n\t\t\t\tif (mTask != null) {\n\t\t\t\t\tmTask.locked = false;\n\t\t\t\t\tToast.makeText(getActivity(), R.string.unlocked, Toast.LENGTH_SHORT).show();\n\t\t\t\t\tif (mLocked) {\n\t\t\t\t\t\tmLocked = false;\n\t\t\t\t\t\tfillUIFromTask();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tpf.show(getParentFragmentManager(), \"unlock_verify\");\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_share) {\n\t\t\t// open the chooser panel to share the note text with an app\n\t\t\tIntent si = getShareIntent();\n\t\t\tif (si != null) this.startActivity(si);\n\t\t\treturn true;\n\t\t}\n\t\treturn super.onOptionsItemSelected(item);\n\t}\n\n\t@Override\n\tpublic void onPrepareOptionsMenu(Menu menu) {\n\t\tmenu.findItem(R.id.menu_timemachine)\n\t\t\t\t.setEnabled(mTask != null && mTask._id > 0 && !isLocked());\n\t\tmenu.findItem(R.id.menu_lock).setVisible(mTask != null && !mTask.locked);\n\t\tmenu.findItem(R.id.menu_unlock).setVisible(mTask != null && mTask.locked);\n\t\tmenu.findItem(R.id.menu_share).setEnabled(!isLocked());\n\n\t\tif (getActivity() instanceof MenuStateController) {\n\t\t\tfinal boolean visible = ((MenuStateController) getActivity()).childItemsVisible();\n\n\t\t\tmenu.setGroupVisible(R.id.editor_menu_group, visible);\n\t\t\t// Outside group to allow for action bar placement\n\t\t\tif (menu.findItem(R.id.menu_delete) != null)\n\t\t\t\tmenu.findItem(R.id.menu_delete).setVisible(visible);\n\t\t\tif (menu.findItem(R.id.menu_revert) != null)\n\t\t\t\tmenu.findItem(R.id.menu_revert).setVisible(visible);\n\t\t\tif (menu.findItem(R.id.menu_share) != null)\n\t\t\t\tmenu.findItem(R.id.menu_share).setVisible(visible);\n\t\t\tif (menu.findItem(R.id.menu_lock) != null)\n\t\t\t\tmenu.findItem(R.id.menu_lock)\n\t\t\t\t\t\t.setVisible(visible && mTask != null && !mTask.locked);\n\t\t\tif (menu.findItem(R.id.menu_unlock) != null)\n\t\t\t\tmenu.findItem(R.id.menu_unlock)\n\t\t\t\t\t\t.setVisible(visible && mTask != null && mTask.locked);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {\n\t\tif (requestCode == 1) {\n\t\t\t// on time travel result\n\t\t\tif (resultCode == Activity.RESULT_OK) {\n\t\t\t\tonTimeTravel(data);\n\t\t\t}\n\t\t}\n\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t}\n\n\tprivate void deleteAndClose() {\n\t\tif (mTask != null && mTask._id > 0 && !isLocked()) {\n\t\t\tDialogDeleteTask.showDialog(getParentFragmentManager(), mTask._id, () -> {\n\t\t\t\t// Prevents save attempts\n\t\t\t\tmTask = null;\n\t\t\t\t// Request a close from activity\n\t\t\t\tif (mListener != null) {\n\t\t\t\t\tmListener.closeFragment(TaskDetailFragment.this);\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\t// Prevents save attempts\n\t\t\tmTask = null;\n\t\t\t// Request a close from activity\n\t\t\tif (mListener != null) {\n\t\t\t\tmListener.closeFragment(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Save mTask to database\n\t */\n\tprivate void saveTask() {\n\t\t// if mTask is null, the task has been deleted or cancelled\n\t\t// If the task is locked, editing is disabled\n\t\tif (mTask == null || isLocked()) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Needed for comparison\n\t\tmTask.setText(taskText.getText().toString());\n\n\t\t// if new item, only save if something has been entered\n\t\tif ((mTask._id > 0 && !mTask.equals(mTaskOrg)) || (mTask._id == -1 && isThereContent())) {\n\t\t\t// mTask.setText(taskText.getText().toString());\n\t\t\tmTask.save(getActivity());\n\t\t\t// Set the intent to open the task.\n\t\t\t// So we dont create a new one on rotation for example\n\t\t\tfixIntent();\n\n\t\t\t// TODO, should restart notification loader for new tasks\n\t\t}\n\t}\n\n\tvoid fixIntent() {\n\t\tstateId = mTask._id;\n\t\tstateListId = mTask.dblist;\n\n\t\tif (getActivity() == null) return;\n\n\t\tfinal Intent orgIntent = getActivity().getIntent();\n\t\tif (orgIntent == null || orgIntent.getAction() == null\n\t\t\t\t|| !orgIntent.getAction().equals(Intent.ACTION_INSERT))\n\t\t\treturn;\n\n\t\tif (mTask == null || mTask._id < 1) return;\n\n\t\tfinal Intent intent = new Intent()\n\t\t\t\t.setAction(Intent.ACTION_EDIT)\n\t\t\t\t.setClass(getActivity(), ActivityMain_.class)\n\t\t\t\t.setData(mTask.getUri())\n\t\t\t\t.putExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, mTask.dblist);\n\n\t\tgetActivity().setIntent(intent);\n\t}\n\n\tboolean isThereContent() {\n\t\tboolean result = false;\n\t\tresult |= taskText.getText().length() > 0;\n\t\tresult |= dueDateBox.getText().length() > 0;\n\t\tresult |= (mTask.locked != mTaskOrg.locked);\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void onPause() {\n\t\tsuper.onPause();\n\t\tif (dontLoad) return;\n\n\t\tsaveTask();\n\t\t// Set locked again\n\t\tmLocked = true;\n\t\t// If task is actually locked, remove text\n\t\tif (isLocked() && mTask != null && taskText != null) {\n\t\t\ttaskText.setText(mTask.title);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onStop() {\n\t\t// Always call the superclass method first\n\t\tsuper.onStop();\n\n\t\t// TODO lazy fix for #412 -> instead, you should stop onLoadFinished()\n\t\t//  when it tries to load reminders that are already there\n\n\t\t// remove all reminders from the list. Next time this Fragment is loaded,\n\t\t// onLoadFinished() will add them back. It MUST be here in onStop(). See\n\t\t// the big comment on top of this file to understand why\n\t\tif (notificationList != null) notificationList.removeAllViews();\n\t}\n\n\t@Override\n\tpublic void onAttach(@NonNull Activity activity) {\n\t\tsuper.onAttach(activity);\n\t\tif (dontLoad) return;\n\n\t\ttry {\n\t\t\tmListener = (OnFragmentInteractionListener) activity;\n\t\t} catch (ClassCastException e) {\n\t\t\t// the activity must implement OnFragmentInteractionListener!\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onDetach() {\n\t\tsuper.onDetach();\n\t\tmListener = null;\n\t}\n\n\t/*\t@Override\n\tpublic void onSaveInstanceState(@NonNull final Bundle bundle_) {\n\t\tsuper.onSaveInstanceState(bundle_);\n\t\tbundle_.putLong(\"stateId\", stateId);\n\t\tbundle_.putLong(\"stateListId\", stateListId);\n\t}\n\n\tprivate void restoreSavedInstanceState_(Bundle savedInstanceState) {\n\t\tif (savedInstanceState == null) return;\n\t\tstateId = savedInstanceState.getLong(\"stateId\");\n\t\tstateListId = savedInstanceState.getLong(\"stateListId\");\n\t}*/\n\n\t/**\n\t * Inserts a notification item in the UI\n\t */\n\t@UiThread(propagation = Propagation.REUSE)\n\tvoid addNotification(final Notification not) {\n\t\tif (getActivity() == null) return;\n\n\t\t// TODO maybe here check if we already have this notification shown, if possible,\n\t\t//  and then either refuse to add this or update the existing one\n\n\t\tView nv = LayoutInflater\n\t\t\t\t.from(getActivity())\n\t\t\t\t.inflate(R.layout.notification_view, null);\n\n\t\t// So we can update the view later\n\t\tnot.view = nv;\n\n\t\t// Setup all the listeners, etc...\n\t\tNotificationItemHelper.setup(this, notificationList, nv, not, mTask);\n\n\t\tnotificationList.addView(nv);\n\t}\n\n\t@Override\n\tpublic void onResume() {\n\t\tsuper.onResume();\n\t\tif (dontLoad) return;\n\n\t\t// Hide data from snoopers\n\t\tif (mTask != null && isLocked()) {\n\t\t\tfillUIFromTask();\n\t\t}\n\t}\n\n\tpublic void onTimeTravel(Intent data) {\n\t\tString restoredText = data.getStringExtra(ActivityTaskHistory.RESULT_TEXT_KEY);\n\t\tif (taskText != null) taskText.setText(restoredText);\n\n\t\t// Need to set here also for password to work\n\t\tif (mTask != null) mTask.setText(restoredText);\n\t}\n\n\t/**\n\t * Returns an appropriately themed {@link TimePickerDialog}, which will be shown\n\t * in a popup, also setting the callback and desired starting time through the\n\t * given parameters. An alternative is\n\t * {@link com.google.android.material.timepicker.MaterialTimePicker}, which is 99%\n\t * identical, but it requires an app theme with parent=\"Theme.MaterialComponents\",\n\t * which does not work in our app, due to the auto-generated code of the annotations\n\t * library\n\t */\n\tpublic TimePickerDialog getTimePickerDialog(Calendar localTime,\n\t\t\t\t\t\t\t\t\t\t\t\tTimePickerDialog.OnTimeSetListener listener) {\n\t\tboolean shouldShowIn24HourMode = DateFormat.is24HourFormat(getActivity());\n\t\treturn new TimePickerDialog(\n\t\t\t\tthis.getActivity(),\n\t\t\t\tlistener, // set the callback for when the user chooses a time\n\t\t\t\tlocalTime.get(Calendar.HOUR_OF_DAY), // set the initial hour & minute\n\t\t\t\tlocalTime.get(Calendar.MINUTE),\n\t\t\t\tshouldShowIn24HourMode);\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/TaskListFragment.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\n\nimport android.app.Activity;\nimport android.content.ClipData;\nimport android.content.ClipboardManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.content.res.Configuration;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.ActionMode;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView.MultiChoiceModeListener;\nimport android.widget.CompoundButton.OnCheckedChangeListener;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.Fragment;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\nimport androidx.preference.PreferenceManager;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.android.material.snackbar.Snackbar;\nimport com.mobeta.android.dslv.DragSortListView;\nimport com.mobeta.android.dslv.DragSortListView.DropListener;\nimport com.mobeta.android.dslv.DragSortListView.RemoveListener;\nimport com.mobeta.android.dslv.SimpleDragSortCursorAdapter;\nimport com.mobeta.android.dslv.SimpleDragSortCursorAdapter.ViewBinder;\nimport com.nononsenseapps.helpers.ListHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.FragmentTaskListBinding;\nimport com.nononsenseapps.notepad.fragments.DialogPassword.PasswordConfirmedListener;\nimport com.nononsenseapps.notepad.interfaces.MenuStateController;\nimport com.nononsenseapps.notepad.interfaces.OnFragmentInteractionListener;\nimport com.nononsenseapps.ui.DateView;\nimport com.nononsenseapps.ui.NoteCheckBox;\nimport com.nononsenseapps.ui.TitleNoteTextView;\n\nimport org.androidannotations.annotations.AfterViews;\nimport org.androidannotations.annotations.Background;\nimport org.androidannotations.annotations.EFragment;\nimport org.androidannotations.annotations.ViewById;\n\nimport java.security.InvalidParameterException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.Executors;\n\n@EFragment(R.layout.fragment_task_list)\npublic class TaskListFragment extends Fragment implements OnSharedPreferenceChangeListener {\n\n\t// Must be less than -1\n\tpublic static final String LIST_ALL_ID_PREF_KEY = \"show_all_tasks_choice_id\";\n\tpublic static final int LIST_ID_ALL = -2;\n\tpublic static final int LIST_ID_OVERDUE = -3;\n\tpublic static final int LIST_ID_TODAY = -4;\n\tpublic static final int LIST_ID_WEEK = -5;\n\n\tpublic static final String LIST_ID = \"list_id\";\n\n\t/**\n\t * {@link android.R.id#list }\n\t */\n\t@ViewById(resName = \"list\")\n\tDragSortListView listView;\n\n\tSimpleSectionsAdapter mAdapter;\n\tprivate long mListId = -1;\n\tprivate OnFragmentInteractionListener mListener;\n\tprivate String mSortType = null;\n\tprivate int mRowCount = 3;\n\tprivate boolean mHideCheckbox = false;\n\tprivate String mListType = null;\n\tprivate LoaderCallbacks<Cursor> mCallback = null;\n\tprivate ActionMode mMode;\n\tprivate boolean mDeleteWasUndone = false;\n\n\t/**\n\t * for {@link R.layout#fragment_task_list}\n\t */\n\tprivate FragmentTaskListBinding mBinding;\n\n\t/*@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tmBinding = FragmentTaskListBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tloadList();\n\t\tsetupPullToRefresh();\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\t*/\n\tpublic static TaskListFragment_ getInstance(final long listId) {\n\t\tTaskListFragment_ f = new TaskListFragment_();\n\t\tBundle args = new Bundle();\n\t\targs.putLong(LIST_ID, listId);\n\t\tf.setArguments(args);\n\t\treturn f;\n\t}\n\n\tpublic TaskListFragment() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tpublic void onCreate(Bundle savedState) {\n\t\tsuper.onCreate(savedState);\n\n\t\tsetHasOptionsMenu(true);\n\n\t\tif (getArguments().getLong(LIST_ID, -1) == -1) {\n\t\t\tthrow new InvalidParameterException(\"Must designate a list to open!\");\n\t\t}\n\t\tmListId = getArguments().getLong(LIST_ID, -1);\n\n\t\t// Start loading data\n\t\tmAdapter = new SimpleSectionsAdapter(getActivity(),\n\t\t\t\tR.layout.tasklist_item_rich,\n\t\t\t\tR.layout.tasklist_header,\n\t\t\t\tnull,\n\t\t\t\tnew String[] { Task.Columns.TITLE, Task.Columns.NOTE,\n\t\t\t\t\t\tTask.Columns.DUE, Task.Columns.COMPLETED,\n\t\t\t\t\t\tTask.Columns.LEFT, Task.Columns.RIGHT },\n\t\t\t\tnew int[] { android.R.id.text1, android.R.id.text1, R.id.date,\n\t\t\t\t\t\tR.id.checkbox, R.id.drag_handle, R.id.dragpadding },\n\t\t\t\t0);\n\n\t\t// Set a drag listener\n\t\tmAdapter.setDropListener((from, to) -> {\n\t\t\tLog.d(\"nononsenseapps drag\", \"Position from \" + from + \" to \" + to);\n\n\t\t\tfinal Task fromTask = new Task((Cursor) mAdapter.getItem(from));\n\t\t\tfinal Task toTask = new Task((Cursor) mAdapter.getItem(to));\n\n\t\t\tfromTask.moveTo(getActivity().getContentResolver(), toTask);\n\t\t});\n\n\t\t/*\n\t\t * listAdapter.setRemoveListener(new RemoveListener() {\n\t\t *\n\t\t * @Override public void remove(int which) { Log.d(TAG, \"Remove pos: \" +\n\t\t * which); Log.d(TAG, \"Remove id: \" + listAdapter.getItemId(which));\n\t\t *\n\t\t * getActivity().getContentResolver().delete(\n\t\t * Uri.withAppendedPath(Task.URI, \"\" + listAdapter.getItemId(which)),\n\t\t * null, null); }\n\t\t *\n\t\t * });\n\t\t */\n\n\t\tmAdapter.setViewBinder(new ViewBinder() {\n\n\t\t\tboolean isHeader = false;\n\n\t\t\tfinal String manualsort = getString(R.string.const_possubsort);\n\t\t\tfinal String notetype = getString(R.string.const_listtype_notes);\n\t\t\tString sTemp = \"\";\n\t\t\tfinal OnCheckedChangeListener checkBoxListener =\n\t\t\t\t\t(buttonView, isChecked) -> Task.setCompleted(\n\t\t\t\t\t\t\tgetActivity(), isChecked, ((NoteCheckBox) buttonView).getNoteId());\n\n\t\t\t@Override\n\t\t\tpublic boolean setViewValue(View view, Cursor c, int colIndex) {\n\t\t\t\t// Check for headers: unlike notes, headers have invalid ids\n\t\t\t\tisHeader = c.getLong(0) == -1;\n\n\t\t\t\tswitch (colIndex) {\n\t\t\t\t\t// Matches order in Task.Columns.Fields\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\t// Title\n\t\t\t\t\t\tsTemp = c.getString(1);\n\t\t\t\t\t\tif (isHeader) {\n\t\t\t\t\t\t\tlong dueDateMillis = c.getLong(4);\n\t\t\t\t\t\t\tsTemp = Task.getHeaderNameForListSortedByDate(sTemp, dueDateMillis,\n\t\t\t\t\t\t\t\t\tgetActivity());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Set height of text for non-headers\n\t\t\t\t\t\t\t((TitleNoteTextView) view).setMaxLines(mRowCount);\n\t\t\t\t\t\t\t// if (mRowCount == 1) {\n\t\t\t\t\t\t\t// ((TitleNoteTextView) view).setSingleLine(true);\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t// else {\n\t\t\t\t\t\t\t// ((TitleNoteTextView) view).setSingleLine(false);\n\t\t\t\t\t\t\t// }\n\n\t\t\t\t\t\t\t// Change color based on complete status\n\t\t\t\t\t\t\t((TitleNoteTextView) view).useSecondaryColor(!c.isNull(3));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t((TitleNoteTextView) view).setTextTitle(sTemp);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\t// Note\n\t\t\t\t\t\tif (!isHeader) {\n\t\t\t\t\t\t\t// Only if task it not locked\n\t\t\t\t\t\t\t// or only one line\n\t\t\t\t\t\t\tif (c.getInt(9) != 1 && mRowCount > 1) {\n\t\t\t\t\t\t\t\t((TitleNoteTextView) view).setTextRest(c.getString(colIndex));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t((TitleNoteTextView) view).setTextRest(\"\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\t// Checkbox\n\t\t\t\t\t\tif (!isHeader) {\n\t\t\t\t\t\t\t((NoteCheckBox) view).setOnCheckedChangeListener(null);\n\t\t\t\t\t\t\t((NoteCheckBox) view).setChecked(!c.isNull(colIndex));\n\t\t\t\t\t\t\t((NoteCheckBox) view).setNoteId(c.getLong(0));\n\t\t\t\t\t\t\t((NoteCheckBox) view).setOnCheckedChangeListener(checkBoxListener);\n\t\t\t\t\t\t\tif (mHideCheckbox\n\t\t\t\t\t\t\t\t\t|| (mListType != null && mListType.equals(notetype))) {\n\t\t\t\t\t\t\t\tview.setVisibility(View.GONE);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tview.setVisibility(View.VISIBLE);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\t// Due date\n\t\t\t\t\t\tif (!isHeader) {\n\t\t\t\t\t\t\t// Always hide for note type\n\t\t\t\t\t\t\tif (mListType != null && mListType.equals(notetype)) {\n\t\t\t\t\t\t\t\tview.setVisibility(View.GONE);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Show for tasks if present\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tif (c.isNull(colIndex)) {\n\t\t\t\t\t\t\t\t\tview.setVisibility(View.GONE);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tview.setVisibility(View.VISIBLE);\n\t\t\t\t\t\t\t\t\t((DateView) view).setTimeText(c.getLong(colIndex));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\t// left, handle\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\t// right, padding\n\t\t\t\t\t\tif (!isHeader) {\n\t\t\t\t\t\t\tif (mSortType != null && mSortType.equals(manualsort)) {\n\t\t\t\t\t\t\t\tview.setVisibility(View.VISIBLE);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tview.setVisibility(View.GONE);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void onActivityCreated(final Bundle state) {\n\t\tsuper.onActivityCreated(state);\n\n\t\t// Get the global list settings\n\t\tfinal SharedPreferences prefs = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\n\t\t// Load pref for item height\n\t\tmRowCount = prefs.getInt(getString(R.string.key_pref_item_max_height), 3);\n\t\tmHideCheckbox = prefs.getBoolean(getString(R.string.pref_hidecheckboxes), false);\n\n\t\tmCallback = new LoaderCallbacks<>() {\n\t\t\t@NonNull\n\t\t\t@Override\n\t\t\tpublic Loader<Cursor> onCreateLoader(int id, Bundle arg1) {\n\t\t\t\tif (id == 0 /* LOADER_CURRENT_LIST */) {\n\t\t\t\t\treturn new CursorLoader(getActivity(), TaskList.getUri(mListId),\n\t\t\t\t\t\t\tTaskList.Columns.FIELDS, null, null, null);\n\t\t\t\t}\n\t\t\t\t// id != 0 => load stuff\n\n\t\t\t\t// What sorting to use\n\t\t\t\tUri targetUri;\n\t\t\t\tString sortSpec;\n\t\t\t\tif (mListType == null) {\n\t\t\t\t\tmListType = prefs.getString(getString(R.string.pref_listtype),\n\t\t\t\t\t\t\tgetString(R.string.default_listtype));\n\t\t\t\t}\n\t\t\t\tif (mSortType == null) {\n\t\t\t\t\tmSortType = prefs.getString(getString(R.string.pref_sorttype),\n\t\t\t\t\t\t\tgetString(R.string.default_sorttype));\n\t\t\t\t}\n\t\t\t\t// analyze the note sorting type chosen by the user\n\t\t\t\tif (mSortType.equals(getString(R.string.const_alphabetic))) {\n\t\t\t\t\ttargetUri = Task.URI;\n\t\t\t\t\tsortSpec = getString(R.string.const_as_alphabetic, Task.Columns.TITLE);\n\t\t\t\t} else if (mSortType.equals(getString(R.string.const_duedate))) {\n\t\t\t\t\ttargetUri = Task.URI_SECTIONED_BY_DATE;\n\t\t\t\t\tsortSpec = null;\n\t\t\t\t} else if (mSortType.equals(getString(R.string.const_modified))) {\n\t\t\t\t\ttargetUri = Task.URI;\n\t\t\t\t\tsortSpec = Task.Columns.UPDATED + \" DESC\";\n\t\t\t\t} else {\n\t\t\t\t\t// manual sorting\n\t\t\t\t\ttargetUri = Task.URI;\n\t\t\t\t\tsortSpec = Task.Columns.LEFT;\n\t\t\t\t}\n\n\t\t\t\tString where;\n\t\t\t\tString[] whereArgs;\n\n\t\t\t\tif (mListId > 0) {\n\t\t\t\t\t// Fix for issue #525 which is caused by some android versions (API 35\n\t\t\t\t\t// emulator, Google Pixel 8a on Android 14, ...) incorrectly generating\n\t\t\t\t\t// the dblist column (=Task.Columns.DBLIST) as BLOB instead of INTEGER.\n\t\t\t\t\t// So we cast its value to INTEGER to restore the (expected) behavior\n\t\t\t\t\t// of older android versions\n\t\t\t\t\twhere = \"CAST(\" + Task.Columns.DBLIST + \" AS INTEGER) IS ?\";\n\t\t\t\t\twhereArgs = new String[] { Long.toString(mListId) };\n\t\t\t\t} else {\n\t\t\t\t\ttargetUri = Task.URI;\n\t\t\t\t\tsortSpec = Task.Columns.DUE;\n\t\t\t\t\twhereArgs = null;\n\t\t\t\t\twhere = Task.Columns.COMPLETED + \" IS NULL\";\n\t\t\t\t\tswitch ((int) mListId) {\n\t\t\t\t\t\tcase LIST_ID_OVERDUE:\n\t\t\t\t\t\t\twhere += andWhereOverdue();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase LIST_ID_TODAY:\n\t\t\t\t\t\t\twhere += andWhereToday();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase LIST_ID_WEEK:\n\t\t\t\t\t\t\t// TODO \"week\" and \"all\" are not on the drawer menu. add them ?\n\t\t\t\t\t\t\twhere += andWhereWeek();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase LIST_ID_ALL:\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t// Show completed also in this case\n\t\t\t\t\t\t\twhere = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn new CursorLoader(getActivity(), targetUri,\n\t\t\t\t\t\tTask.Columns.FIELDS, where, whereArgs, sortSpec);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor c) {\n\t\t\t\tif (loader.getId() == 0) {\n\t\t\t\t\tif (c != null && c.moveToFirst()) {\n\t\t\t\t\t\tfinal TaskList list = new TaskList(c);\n\t\t\t\t\t\tmSortType = list.sorting;\n\t\t\t\t\t\tmListType = list.listtype;\n\t\t\t\t\t\t// Reload tasks with new sorting\n\t\t\t\t\t\tLoaderManager.getInstance(TaskListFragment.this)\n\t\t\t\t\t\t\t\t.restartLoader(1, null, this);\n\t\t\t\t\t}\n\t\t\t\t} else { // loader.getId() == LOADER_TASKS\n\t\t\t\t\tmAdapter.swapCursor(c);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> loader) {\n\t\t\t\tif (loader.getId() == 0) {\n\t\t\t\t\t// Nothing to do\n\t\t\t\t} else {\n\t\t\t\t\tmAdapter.swapCursor(null);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tif (mListId > 0) {\n\t\t\tLoaderManager.getInstance(this).restartLoader(0, null, mCallback);\n\t\t} else {\n\t\t\t// Setting sort types for all tasks always to due date\n\t\t\tmSortType = getString(R.string.const_duedate);\n\t\t\tLoaderManager.getInstance(this).restartLoader(1, null, mCallback);\n\t\t}\n\t}\n\n\tpublic static String whereOverDue() {\n\t\treturn Task.Columns.DUE + \" BETWEEN \" + Task.OVERDUE + \" AND \" + Task.TODAY_START;\n\t}\n\n\tpublic static String andWhereOverdue() {\n\t\treturn \" AND \" + whereOverDue();\n\t}\n\n\tpublic static String whereToday() {\n\t\treturn Task.Columns.DUE + \" BETWEEN \" + Task.TODAY_START + \" AND \"\n\t\t\t\t+ Task.TODAY_PLUS(1);\n\t}\n\n\tpublic static String andWhereToday() {\n\t\treturn \" AND \" + whereToday();\n\t}\n\n\tpublic static String whereWeek() {\n\t\treturn Task.Columns.DUE + \" BETWEEN \" + Task.TODAY_START + \" AND (\"\n\t\t\t\t+ Task.TODAY_PLUS(5) + \" -1)\";\n\t}\n\n\tpublic static String andWhereWeek() {\n\t\treturn \" AND \" + whereWeek();\n\t}\n\n\t@AfterViews\n\tvoid setupPullToRefresh() {\n\t\t// every list gets its own instance of the swipetorefresh layout\n\t\tvar ptrL = (SwipeRefreshLayout) this.getView().findViewById(R.id.ptrLayout);\n\n\t\t// The pull-to-refresh layout is defined in fragment_task_list.xml\n\n\t\t// now we add it to ActivityMain, which will take care of it\n\t\t((ActivityMain_) getActivity()).addSwipeRefreshLayoutToList(ptrL);\n\t}\n\n\t@AfterViews\n\tvoid loadList() {\n\t\tlistView.setAdapter(mAdapter);\n\n\t\tlistView.setOnItemClickListener((arg0, origin, pos, id) -> {\n\t\t\tif (mListener != null && id > 0) {\n\t\t\t\tmListener.onFragmentInteraction(Task.getUri(id), mListId, origin);\n\t\t\t}\n\t\t});\n\n\t\tlistView.setOnItemLongClickListener((arg0, view, pos, id) -> {\n\t\t\tlistView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);\n\t\t\t// Also select the item in question\n\t\t\tlistView.setItemChecked(pos, true);\n\t\t\treturn true;\n\t\t});\n\n\t\t// TODO this MultiChoiceModeListener occupies the next 220 lines.\n\t\t//  It handles the logic for when multiple notes are selected and a button on\n\t\t//  the action bar is pressed. Put this class in its own java file\n\t\tlistView.setMultiChoiceModeListener(new MultiChoiceModeListener() {\n\n\t\t\tfinal HashMap<Long, Task> tasks = new HashMap<>();\n\n\t\t\t/**\n\t\t\t * Delete tasks and display a snackbar with an undo action\n\t\t\t */\n\t\t\tprivate void deleteTasks(final Map<Long, Task> taskMap) {\n\t\t\t\tfinal Task[] tasks = taskMap.values().toArray(new Task[0]);\n\n\t\t\t\t// If any are locked, ask for password first\n\t\t\t\tfinal boolean locked = PreferencesHelper.isPasswordSet(getActivity());\n\n\t\t\t\t// Reset undo flag\n\t\t\t\tmDeleteWasUndone = false;\n\n\t\t\t\t// Dismiss callback\n\t\t\t\tfinal Snackbar.Callback dismissCallback = new Snackbar.Callback() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onDismissed(Snackbar snackbar, int event) {\n\t\t\t\t\t\t// Do nothing if dismissed because action was pressed\n\t\t\t\t\t\t// Dismiss wil be called more than once if undo is pressed\n\t\t\t\t\t\tif (Snackbar.Callback.DISMISS_EVENT_ACTION != event && !mDeleteWasUndone) {\n\t\t\t\t\t\t\t// Delete them\n\t\t\t\t\t\t\tExecutors.newSingleThreadExecutor().execute(() -> {\n\t\t\t\t\t\t\t\t// Background work here\n\t\t\t\t\t\t\t\tfor (Task t : tasks) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tt.delete(getActivity());\n\t\t\t\t\t\t\t\t\t} catch (Exception ignored) {}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\t// Undo callback\n\t\t\tfinal View.OnClickListener undoListener = new View.OnClickListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\tmDeleteWasUndone = true;\n\t\t\t\t\t// Returns removed items to view\n\t\t\t\t\tmAdapter.reset();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tfinal PasswordConfirmedListener pListener = new PasswordConfirmedListener() {\n\t\t\t\t@Override\n\t\t\t\t@Background\n\t\t\t\tpublic void onPasswordConfirmed() {\n\t\t\t\t\tfor (final Task t : tasks.values()) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tt.delete(getActivity());\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tNnnLogger.warning(TaskListFragment.class, \"Can't delete task\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tString msg;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmsg = getResources().getQuantityString(R.plurals.notedeleted_msg,\n\t\t\t\t\t\t\t\ttasks.size(), tasks.size());\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Protect against faulty translations\n\t\t\t\t\t\tmsg = getResources().getString(R.string.deleted);\n\t\t\t\t\t}\n\n\t\t\t\t\t// TODO should use a Snackbar instead of Toasts\n\t\t\t\t\t// Snackbar\n\t\t\t\t\t//     .make(mFab, msg, Snackbar.LENGTH_LONG)\n\t\t\t\t\t//     .setAction(R.string.undo, listener)\n\t\t\t\t\t//     .setCallback(dismissCallback)\n\t\t\t\t\t//     .show();\n\t\t\t\t\tToast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();\n\n\t\t\t\t\tif (mMode != null) mMode.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t@Override\n\t\t\tpublic boolean onPrepareActionMode(ActionMode mode, Menu menu) {\n\t\t\t\t// Here you can perform updates to the CAB due to\n\t\t\t\t// an invalidate() request\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onDestroyActionMode(ActionMode mode) {\n\t\t\t\t// Here you can make any necessary updates to the activity when\n\t\t\t\t// the CAB is removed. By default, selected items are\n\t\t\t\t// deselected/unchecked.\n\t\t\t\ttasks.clear();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean onCreateActionMode(ActionMode mode, Menu menu) {\n\t\t\t\t// Must setup the contextual action menu\n\t\t\t\tgetActivity().getMenuInflater().inflate(R.menu.fragment_tasklist_context, menu);\n\n\t\t\t\t// Must clear for reuse\n\t\t\t\ttasks.clear();\n\n\t\t\t\t// For password\n\t\t\t\tmMode = mode;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * When the user presses a button on the action bar, this function decides what to do\n\t\t\t * with the selected notes: copy, delete, share, or move to another list\n\t\t\t */\n\t\t\t@Override\n\t\t\tpublic boolean onActionItemClicked(final ActionMode mode, MenuItem item) {\n\t\t\t\t// Respond to clicks on the actions in the CAB\n\t\t\t\tboolean finish = false;\n\t\t\t\tint itemId = item.getItemId();\n\t\t\t\tif (itemId == R.id.menu_copy) {\n\t\t\t\t\tfinal ClipboardManager clipboard = (ClipboardManager) getActivity()\n\t\t\t\t\t\t\t.getSystemService(Context.CLIPBOARD_SERVICE);\n\t\t\t\t\tclipboard.setPrimaryClip(ClipData.newPlainText(\n\t\t\t\t\t\t\tgetString(R.string.app_name_short), getShareText()));\n\t\t\t\t\ttry {\n\t\t\t\t\t\tToast.makeText(getActivity(), getResources().getQuantityString(\n\t\t\t\t\t\t\t\t\t\tR.plurals.notecopied_msg, tasks.size(), tasks.size()),\n\t\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Protect against faulty translations\n\t\t\t\t\t}\n\t\t\t\t\tfinish = true;\n\t\t\t\t} else if (itemId == R.id.menu_delete) {\n\t\t\t\t\tboolean locked = false;\n\t\t\t\t\tfor (final Task t : tasks.values()) {\n\t\t\t\t\t\tif (t.locked) {\n\t\t\t\t\t\t\tlocked = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (locked) {\n\t\t\t\t\t\tDialogPassword delpf = new DialogPassword();\n\t\t\t\t\t\tdelpf.setListener(pListener);\n\t\t\t\t\t\tdelpf.show(getParentFragmentManager(), \"multi_delete_verify\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tDialogDeleteTask.showDialog(getParentFragmentManager(), -1,\n\t\t\t\t\t\t\t\tpListener::onPasswordConfirmed);\n\t\t\t\t\t}\n\t\t\t\t} else if (itemId == R.id.menu_switch_list) {\n\t\t\t\t\t// show move to list dialog\n\t\t\t\t\tDialogMoveToList.getInstance(\n\t\t\t\t\t\t\t\t\ttasks.keySet().toArray(new Long[0]))\n\t\t\t\t\t\t\t.show(getParentFragmentManager(), \"move_to_list_dialog\");\n\t\t\t\t\tfinish = true;\n\t\t\t\t} else if (itemId == R.id.menu_share) {\n\t\t\t\t\tstartActivity(getShareIntent());\n\t\t\t\t\tfinish = true;\n\t\t\t\t} else {\n\t\t\t\t\tfinish = false;\n\t\t\t\t}\n\n\t\t\t\tif (finish) mode.finish(); // Action picked, so close the CAB\n\t\t\t\treturn finish;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onItemCheckedStateChanged(ActionMode mode,\n\t\t\t\t\t\t\t\t\t\t\t\t  int position, long id, boolean checked) {\n\t\t\t\tif (checked) {\n\t\t\t\t\ttasks.put(id, new Task((Cursor) listView.getAdapter()\n\t\t\t\t\t\t\t.getItem(position)));\n\t\t\t\t} else {\n\t\t\t\t\ttasks.remove(id);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\t// Only show the title string on screens that are wide enough,\n\t\t\t\t\t// for example large screens or if you are in landscape\n\t\t\t\t\tfinal Configuration conf = getResources()\n\t\t\t\t\t\t\t.getConfiguration();\n\t\t\t\t\tif (conf.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE)\n\t\t\t\t\t\t\t|| conf.orientation == Configuration.ORIENTATION_LANDSCAPE) {\n\t\t\t\t\t\tmode.setTitle(getResources().getQuantityString(\n\t\t\t\t\t\t\t\tR.plurals.mode_choose, tasks.size(),\n\t\t\t\t\t\t\t\ttasks.size()));\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Protect against faulty translations\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getShareText() {\n\t\t\t\tfinal StringBuilder sb = new StringBuilder();\n\t\t\t\tfor (Task t : tasks.values()) {\n\t\t\t\t\tif (sb.length() > 0) {\n\t\t\t\t\t\tsb.append(\"\\n\\n\");\n\t\t\t\t\t}\n\t\t\t\t\tif (t.locked) {\n\t\t\t\t\t\tsb.append(t.title);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsb.append(t.getText());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn sb.toString();\n\t\t\t}\n\n\t\t\t// when sharing many notes from the list view,\n\t\t\t// we send a list of their titles as subject\n\t\t\tString getShareSubject() {\n\t\t\t\tStringBuilder result = new StringBuilder();\n\t\t\t\tfor (Task t : tasks.values()) {\n\t\t\t\t\tresult.append(\", \").append(t.title);\n\t\t\t\t}\n\t\t\t\t// if necessary, remove the first \", \"\n\t\t\t\treturn (result.length() == 0) ? \"\" : result.substring(2);\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * When you select multiple notes, and presses 'share' on the menu,\n\t\t\t * this function creates the intent to call Android's app picker to choose\n\t\t\t * who will receive the shared notes' content\n\t\t\t */\n\t\t\tIntent getShareIntent() {\n\t\t\t\tfinal Intent shareIntent = new Intent(Intent.ACTION_SEND);\n\t\t\t\tshareIntent.setType(\"text/plain\");\n\t\t\t\tshareIntent.putExtra(Intent.EXTRA_TEXT, getShareText());\n\t\t\t\tshareIntent.putExtra(Intent.EXTRA_SUBJECT, getShareSubject());\n\t\t\t\tshareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);\n\t\t\t\treturn shareIntent;\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {\n\t\tinflater.inflate(R.menu.fragment_tasklist, menu);\n\t}\n\n\t@Override\n\tpublic void onPrepareOptionsMenu(@NonNull Menu menu) {\n\t\tif (getActivity() instanceof MenuStateController) {\n\t\t\tfinal boolean visible = ((MenuStateController) getActivity())\n\t\t\t\t\t.childItemsVisible();\n\n\t\t\tmenu.setGroupVisible(R.id.list_menu_group, visible);\n\t\t\tif (!visible) {\n\t\t\t\tif (mMode != null) mMode.finish();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tint itemId = item.getItemId();\n\t\tif (itemId == R.id.menu_add) {\n\t\t\tif (mListener != null && mListId > 0) {\n\t\t\t\tmListener.addTaskInList(\"\", mListId);\n\t\t\t} else if (mListener != null) {\n\t\t\t\tmListener.addTaskInList(\"\", ListHelper.getARealList(getActivity(), -1));\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_clearcompleted) {\n\t\t\tif (mListId != -1) {\n\t\t\t\tDialogDeleteCompletedTasks\n\t\t\t\t\t\t.showDialog(getParentFragmentManager(), mListId, null);\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_sort_title) {\n\t\t\t// TODO reorder the notes like we do in DialogEditList\n\t\t\tToast.makeText(this.getContext(), R.string.feature_is_WIP, Toast.LENGTH_SHORT).show();\n\t\t\t// SharedPreferencesHelper.setSortingAlphabetic(this);\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_sort_due) {\n\t\t\tToast.makeText(this.getContext(), R.string.feature_is_WIP, Toast.LENGTH_SHORT).show();\n\t\t\t// SharedPreferencesHelper.setSortingDue(this);\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_sort_manual) {\n\t\t\tToast.makeText(this.getContext(), R.string.feature_is_WIP, Toast.LENGTH_SHORT).show();\n\t\t\t// SharedPreferencesHelper.setSortingManual(this);\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onSaveInstanceState(@NonNull Bundle outState) {\n\t\tsuper.onSaveInstanceState(outState);\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\tsuper.onDestroy();\n\t\tLoaderManager.getInstance(this).destroyLoader(0);\n\t}\n\n\t@Override\n\tpublic void onAttach(@NonNull Activity activity) {\n\t\tsuper.onAttach(activity);\n\t\ttry {\n\t\t\tmListener = (OnFragmentInteractionListener) activity;\n\t\t} catch (ClassCastException e) {\n\t\t\t// the activity must implement OnFragmentInteractionListener!\n\t\t}\n\n\t\t// We want to be notified of future changes to auto refresh\n\t\tPreferenceManager.getDefaultSharedPreferences(getActivity())\n\t\t\t\t.registerOnSharedPreferenceChangeListener(this);\n\t}\n\n\n\t@Override\n\tpublic void onDetach() {\n\t\tmListener = null;\n\t\tPreferenceManager.getDefaultSharedPreferences(getActivity())\n\t\t\t\t.unregisterOnSharedPreferenceChangeListener(this);\n\t\tsuper.onDetach();\n\t}\n\n\tstatic class SimpleSectionsAdapter extends SimpleDragSortCursorAdapter {\n\t\tDropListener dropListener = null;\n\t\tRemoveListener removeListener = null;\n\t\tfinal int mItemLayout;\n\t\tfinal int mHeaderLayout;\n\t\tfinal static int itemType = 0;\n\t\tfinal static int headerType = 1;\n\t\tfinal SharedPreferences prefs;\n\t\tfinal Context context;\n\n\t\tpublic SimpleSectionsAdapter(Context context, int layout,\n\t\t\t\t\t\t\t\t\t int headerLayout, Cursor c, String[] from, int[] to, int flags) {\n\t\t\tsuper(context, layout, c, from, to, flags);\n\t\t\tthis.context = context;\n\t\t\tmItemLayout = layout;\n\t\t\tmHeaderLayout = headerLayout;\n\t\t\tprefs = PreferenceManager.getDefaultSharedPreferences(context);\n\t\t}\n\n\t\tint getViewLayout(final int position) {\n\t\t\tif (itemType == getItemViewType(position)) {\n\t\t\t\treturn mItemLayout;\n\t\t\t} else {\n\t\t\t\treturn mHeaderLayout;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void remove(int which) {\n\t\t\tif (removeListener != null) removeListener.remove(which);\n\t\t\tsuper.remove(which);\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void drop(int from, int to) {\n\t\t\t// Call any listener that has been defined\n\t\t\tif (dropListener != null) dropListener.drop(from, to);\n\t\t\t// Call super to handle UI mapping (for smoothness)\n\t\t\tsuper.drop(from, to);\n\t\t}\n\n\t\tpublic void setDropListener(DropListener dropListener) {\n\t\t\tthis.dropListener = dropListener;\n\t\t}\n\n\t\tpublic void setRemoveListener(RemoveListener removeListener) {\n\t\t\tthis.removeListener = removeListener;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getViewTypeCount() {\n\t\t\treturn 2;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getItemViewType(int position) {\n\t\t\tif (position == -1) {\n\t\t\t\t// there was an error in drag-sort-listview: the cached view for dragging\n\t\t\t\t// the note is too high (because the note is too long: ~90 lines).\n\t\t\t\t// c.getLong(0) will crash anyway, because -1 is an invalid index.\n\t\t\t\t// the fix is in SimpleFloatViewManager.onCreateFloatView()\n\t\t\t\tNnnLogger.error(TaskListFragment.class, \"Invalid index -1, now I'll crash\");\n\t\t\t}\n\t\t\tfinal Cursor c = (Cursor) getItem(position);\n\t\t\t// If the id is invalid, it's a header\n\t\t\tif (c.getLong(0) < 1) {\n\t\t\t\treturn headerType;\n\t\t\t} else {\n\t\t\t\treturn itemType;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\t\tif (convertView == null) {\n\t\t\t\tconvertView = LayoutInflater\n\t\t\t\t\t\t.from(this.context)\n\t\t\t\t\t\t.inflate(getViewLayout(position), parent, false);\n\t\t\t\tif (itemType == getItemViewType(position)) {\n\t\t\t\t\tsetPrefsOnView(convertView.findViewById(android.R.id.text1));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn super.getView(position, convertView, parent);\n\t\t}\n\n\t\tprivate void setPrefsOnView(final TitleNoteTextView view) {\n\t\t\tString fontPref1 = prefs.getString(context.getString(R.string.pref_list_title_fontfamily), \"1\");\n\t\t\tview.setTitleFontFamily(Integer.parseInt(fontPref1));\n\n\t\t\tString fontPref2 = prefs.getString(context.getString(R.string.pref_list_title_fontstyle), \"1\");\n\t\t\tview.setTitleFontStyle(Integer.parseInt(fontPref2));\n\n\t\t\tString fontPref3 = prefs.getString(context.getString(R.string.pref_list_body_fontfamily), \"0\");\n\t\t\tview.setBodyFontFamily(Integer.parseInt(fontPref3));\n\n\t\t\tboolean shouldShowLinks = prefs.getBoolean(context.getString(R.string.pref_list_links), true);\n\t\t\tview.setLinkify(shouldShowLinks);\n\n\t\t\tString fontPref4 = prefs.getString(context.getString(R.string.pref_list_fontsize), \"1\");\n\t\t\tview.setTheTextSize(Integer.parseInt(fontPref4));\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {\n\t\tif (isDetached()) {\n\t\t\t// Fix crash report\n\t\t\treturn;\n\t\t}\n\t\tif (key == null) {\n\t\t\t// it happens sometimes during Espresso tests\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tboolean reload = false;\n\t\t\tif (key.equals(getString(R.string.pref_sorttype))) {\n\t\t\t\tmSortType = null;\n\t\t\t\treload = true;\n\t\t\t} else if (key.equals(getString(R.string.key_pref_item_max_height))) {\n\t\t\t\tmRowCount = prefs.getInt(key, 3);\n\t\t\t\treload = true;\n\t\t\t} else if (key.equals(getString(R.string.pref_hidecheckboxes))) {\n\t\t\t\tmHideCheckbox = prefs.getBoolean(key, false);\n\t\t\t\treload = true;\n\t\t\t} else if (key.equals(getString(R.string.pref_listtype))) {\n\t\t\t\tmListType = null;\n\t\t\t\treload = true;\n\t\t\t}\n\n\t\t\tif (reload && mCallback != null) {\n\t\t\t\tLoaderManager.getInstance(this).restartLoader(0, null, mCallback);\n\t\t\t}\n\t\t} catch (IllegalStateException ignored) {\n\t\t\t// Fix crash report\n\t\t\t// Might get a race condition where fragment is detached when getString is called\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/TaskListViewPagerFragment.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.fragments;\n\nimport android.app.SearchManager;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.database.Cursor;\nimport android.database.DataSetObserver;\nimport android.os.Bundle;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AutoCompleteTextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.cursoradapter.widget.CursorAdapter;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentPagerAdapter;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\nimport androidx.preference.PreferenceManager;\nimport androidx.viewpager.widget.ViewPager;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.activities.ActivitySearchDeleted;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.FragmentTasklistViewpagerBinding;\nimport com.nononsenseapps.notepad.fragments.DialogEditList.EditListDialogListener;\nimport com.nononsenseapps.notepad.interfaces.ListOpener;\nimport com.nononsenseapps.notepad.interfaces.MenuStateController;\nimport com.nononsenseapps.ui.ViewsHelper;\n\nimport org.androidannotations.annotations.AfterViews;\nimport org.androidannotations.annotations.EFragment;\nimport org.androidannotations.annotations.ViewById;\n\nimport java.util.Objects;\n\n/**\n * Displays many listfragments across a viewpager. Supports selecting a certain one on startup\n */\n@EFragment(R.layout.fragment_tasklist_viewpager)\npublic class TaskListViewPagerFragment extends Fragment implements\n\t\tEditListDialogListener, ListOpener {\n\n\tpublic static final String START_LIST_ID = \"start_list_id\";\n\n\t@ViewById(resName = \"pager\")\n\tViewPager pager;\n\n\tprivate SectionsPagerAdapter mSectionsPagerAdapter;\n\tSimpleCursorAdapter mTaskListsAdapter;\n\n\t// boolean firstLoad = true;\n\n\tprivate long mListIdToSelect = -1;\n\n\t/**\n\t * If transitions between note lists should be animated, with smooth scrolling\n\t * The value is regularly updated by {@link TaskListViewPagerFragment#onResume()}\n\t */\n\tprivate boolean mShouldAnimate = true;\n\n\t/**\n\t * for {@link R.layout#fragment_tasklist_viewpager}\n\t */\n\tprivate FragmentTasklistViewpagerBinding mBinding;\n\n\t/*@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tmBinding = FragmentTasklistViewpagerBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tsetAdapter();\n\t}\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tmBinding = null;\n\t}\n\t*/\n\tpublic static TaskListViewPagerFragment getInstance() {\n\t\treturn getInstance(-1);\n\t}\n\n\tpublic static TaskListViewPagerFragment getInstance(final long startListId) {\n\t\tTaskListViewPagerFragment_ f = new TaskListViewPagerFragment_();\n\t\tBundle args = new Bundle();\n\t\targs.putLong(START_LIST_ID, startListId);\n\t\tf.setArguments(args);\n\t\treturn f;\n\t}\n\n\tpublic TaskListViewPagerFragment() {\n\t\tsuper();\n\t}\n\n\tpublic SectionsPagerAdapter getSectionsPagerAdapter() {\n\t\treturn mSectionsPagerAdapter;\n\t}\n\n\t@Override\n\tpublic void onResume() {\n\t\tsuper.onResume();\n\t\tmShouldAnimate = PreferencesHelper.areAnimationsEnabled(this.getContext());\n\t}\n\n\t@Override\n\tpublic void onCreate(Bundle savedState) {\n\t\tsuper.onCreate(savedState);\n\t\tsetHasOptionsMenu(true);\n\n\t\tmListIdToSelect = getArguments().getLong(START_LIST_ID, -1);\n\t\tNnnLogger.debug(TaskDetailFragment.class, \"onCreate: \" + savedState);\n\t\tif (savedState != null) {\n\t\t\tmListIdToSelect = savedState.getLong(START_LIST_ID);\n\t\t}\n\n\t\t// Adapter for list titles and ids\n\t\tmTaskListsAdapter = new SimpleCursorAdapter(getActivity(),\n\t\t\t\tandroid.R.layout.simple_dropdown_item_1line, null,\n\t\t\t\tnew String[] { TaskList.Columns.TITLE },\n\t\t\t\tnew int[] { android.R.id.text1 }, 0);\n\t\t// Adapter for view pager\n\t\tmSectionsPagerAdapter = new SectionsPagerAdapter(\n\t\t\t\tgetChildFragmentManager(), mTaskListsAdapter);\n\t}\n\n\t@Override\n\tpublic void onActivityCreated(final Bundle state) {\n\t\tsuper.onActivityCreated(state);\n\n\t\tLoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<>() {\n\n\t\t\t@NonNull\n\t\t\t@Override\n\t\t\tpublic Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {\n\t\t\t\treturn new CursorLoader(getActivity(), TaskList.URI,\n\t\t\t\t\t\tnew String[] { TaskList.Columns._ID,\n\t\t\t\t\t\t\t\tTaskList.Columns.TITLE }, null, null,\n\t\t\t\t\t\tgetResources().getString(R.string.const_as_alphabetic,\n\t\t\t\t\t\t\t\tTaskList.Columns.TITLE));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> arg0, Cursor c) {\n\t\t\t\tmTaskListsAdapter.swapCursor(c);\n\t\t\t\tfinal int pos;\n\t\t\t\tif (mListIdToSelect != -1) {\n\t\t\t\t\tpos = mSectionsPagerAdapter.getItemPosition(mListIdToSelect);\n\t\t\t\t} else {\n\t\t\t\t\tpos = -1;\n\t\t\t\t}\n\t\t\t\tif (pos >= 0) {\n\t\t\t\t\tpager.setCurrentItem(pos, mShouldAnimate);\n\t\t\t\t\tmListIdToSelect = -1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> arg0) {\n\t\t\t\tmTaskListsAdapter.swapCursor(null);\n\t\t\t}\n\t\t};\n\n\t\t// Load actual data\n\t\tLoaderManager.getInstance(this).restartLoader(0, null, loaderCallbacks);\n\t}\n\n\t@AfterViews\n\tvoid setAdapter() {\n\t\t// Set space between fragments\n\t\tpager.setPageMargin(ViewsHelper.convertDip2Pixels(getActivity(), 16));\n\t\t// Set adapters\n\t\tpager.setAdapter(mSectionsPagerAdapter);\n\n\t}\n\n\t/**\n\t * Create the {@link SearchView} to find notes while browsing {@link ActivityMain}\n\t */\n\t@Override\n\tpublic void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {\n\t\tinflater.inflate(R.menu.fragment_tasklists_viewpager, menu);\n\n\t\tif (menu.findItem(R.id.menu_search) == null) {\n\t\t\treturn;\n\t\t}\n\t\tSearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();\n\t\tif (searchView == null) {\n\t\t\treturn;\n\t\t}\n\t\t// Assumes current activity is the searchable activity\n\t\tSearchManager sMan = this.requireActivity().getSystemService(SearchManager.class);\n\t\tsearchView.setSearchableInfo(sMan.getSearchableInfo(requireActivity().getComponentName()));\n\t\t// expand the searchview by default when the user clicks on the icon\n\t\tsearchView.setIconifiedByDefault(false);\n\t\tsearchView.setQueryRefinementEnabled(false);\n\t\tsearchView.setSubmitButtonEnabled(false);\n\n\t\t// widen the suggestions popup so that it occupies the whole screen\n\t\tvar autoCompTxtVi = (AutoCompleteTextView) searchView\n\t\t\t\t.findViewById(androidx.appcompat.R.id.search_src_text);\n\t\tfinal View dropDownSugg = searchView.findViewById(autoCompTxtVi.getDropDownAnchor());\n\t\tif (dropDownSugg == null) return;\n\t\tdropDownSugg.addOnLayoutChangeListener(\n\t\t\t\t(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {\n\t\t\t\t\t// gives more horizontal space, to show more text of a search suggestion item\n\t\t\t\t\tautoCompTxtVi.setDropDownWidth(ViewGroup.LayoutParams.MATCH_PARENT);\n\t\t\t\t});\n\t}\n\n\t@Override\n\tpublic void onPrepareOptionsMenu(@NonNull Menu menu) {\n\t\tif (!(getActivity() instanceof MenuStateController)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfinal boolean visible = ((MenuStateController) getActivity()).childItemsVisible();\n\t\tmenu.setGroupVisible(R.id.viewpager_menu_group, visible);\n\n\t\t// Outside group to allow for action bar placement\n\t\tif (menu.findItem(R.id.menu_search) != null)\n\t\t\tmenu.findItem(R.id.menu_search).setVisible(visible);\n\t\tif (menu.findItem(R.id.menu_sync) != null)\n\t\t\tmenu.findItem(R.id.menu_sync).setVisible(visible);\n\t}\n\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tint itemId = item.getItemId();\n\t\tif (itemId == R.id.menu_search) {\n\t\t\t// Always visible, but do this if not visible\n\t\t\t// getActivity().onSearchRequested();\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.menu_deletedtasks) {\n\t\t\tstartActivity(new Intent(getActivity(), ActivitySearchDeleted.class));\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onFinishEditDialog(final long id) {\n\t\topenList(id);\n\t}\n\n\t@Override\n\tpublic void openList(final long id) {\n\t\t// If it fails, will load on refresh\n\t\tmListIdToSelect = id;\n\t\tNnnLogger.debug(TaskListViewPagerFragment.class, \"openList: \" + mListIdToSelect);\n\t\tif (mSectionsPagerAdapter != null) {\n\n\t\t\tfinal int pos;\n\t\t\tif (id < 1)\n\t\t\t\tpos = 0;\n\t\t\telse\n\t\t\t\tpos = mSectionsPagerAdapter.getItemPosition(id);\n\n\t\t\tif (pos > -1) {\n\t\t\t\tpager.setCurrentItem(pos, mShouldAnimate);\n\t\t\t\tmListIdToSelect = -1;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onSaveInstanceState(@NonNull Bundle outState) {\n\t\tsuper.onSaveInstanceState(outState);\n\t\t// even if 'pager' is tagged @NonNull, but it may actually be null, for example when\n\t\t// you load the task history activity\n\t\tif (mTaskListsAdapter != null && Objects.nonNull(pager)) {\n\t\t\tlong id = mTaskListsAdapter.getItemId(pager.getCurrentItem());\n\t\t\toutState.putLong(START_LIST_ID, id);\n\t\t\tNnnLogger.debug(TaskListViewPagerFragment.class, \"Saved state, id=\" + id);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\tif (mSectionsPagerAdapter != null) {\n\t\t\tmSectionsPagerAdapter.destroy();\n\t\t}\n\t\tLoaderManager.getInstance(this).destroyLoader(0);\n\n\t\tsuper.onDestroy();\n\t}\n\n\tpublic class SectionsPagerAdapter extends FragmentPagerAdapter {\n\n\t\tprivate final CursorAdapter wrappedAdapter;\n\t\tprivate final DataSetObserver subObserver;\n\t\tprivate final OnSharedPreferenceChangeListener prefListener;\n\n\t\tprivate long all_id = -2;\n\n\t\tpublic SectionsPagerAdapter(final FragmentManager fm,\n\t\t\t\t\t\t\t\t\tfinal CursorAdapter wrappedAdapter) {\n\t\t\tsuper(fm);\n\t\t\tthis.wrappedAdapter = wrappedAdapter;\n\n\t\t\tsubObserver = new DataSetObserver() {\n\t\t\t\t@Override\n\t\t\t\tpublic void onChanged() {\n\t\t\t\t\tnotifyDataSetChanged();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onInvalidated() {\n\t\t\t\t\t// Probably destroying the loader\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (wrappedAdapter != null)\n\t\t\t\twrappedAdapter.registerDataSetObserver(subObserver);\n\n\t\t\t// also monitor changes of all tasks choice\n\t\t\tfinal SharedPreferences prefs = PreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(getActivity());\n\n\t\t\tprefListener = (sharedPreferences, key) -> {\n\t\t\t\tif (TaskListFragment.LIST_ALL_ID_PREF_KEY.equals(key)) {\n\t\t\t\t\tall_id = prefs.getLong(\n\t\t\t\t\t\t\tTaskListFragment.LIST_ALL_ID_PREF_KEY,\n\t\t\t\t\t\t\tTaskListFragment.LIST_ID_WEEK);\n\t\t\t\t\tnotifyDataSetChanged();\n\t\t\t\t}\n\t\t\t};\n\t\t\tprefs.registerOnSharedPreferenceChangeListener(prefListener);\n\n\t\t\t// Set all value\n\t\t\tall_id = prefs.getLong(TaskListFragment.LIST_ALL_ID_PREF_KEY,\n\t\t\t\t\tTaskListFragment.LIST_ID_WEEK);\n\t\t}\n\n\t\tpublic void destroy() {\n\t\t\tif (wrappedAdapter != null) {\n\t\t\t\twrappedAdapter.unregisterDataSetObserver(subObserver);\n\t\t\t}\n\t\t\tif (prefListener != null) {\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(getActivity())\n\t\t\t\t\t\t.unregisterOnSharedPreferenceChangeListener(prefListener);\n\t\t\t}\n\t\t}\n\n\t\t@NonNull\n\t\t@Override\n\t\tpublic Fragment getItem(int pos) {\n\t\t\tlong id = getItemId(pos);\n\t\t\t// if (id < 0) return null;\n\t\t\treturn TaskListFragment_.getInstance(id);\n\t\t}\n\n\t\t@Override\n\t\tpublic long getItemId(int position) {\n\t\t\tlong id = all_id;\n\t\t\tif (wrappedAdapter != null && position > 0) {\n\t\t\t\tCursor c = (Cursor) wrappedAdapter.getItem(position - 1);\n\t\t\t\tif (c != null && !c.isAfterLast() && !c.isBeforeFirst()) {\n\t\t\t\t\tid = c.getLong(0);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn id;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getCount() {\n\t\t\tif (wrappedAdapter != null)\n\t\t\t\treturn 1 + wrappedAdapter.getCount();\n\t\t\telse\n\t\t\t\treturn 1;\n\t\t}\n\n\t\t@Override\n\t\tpublic CharSequence getPageTitle(int position) {\n\t\t\tif (position >= getCount()) return null;\n\t\t\tCharSequence title = \"\";\n\n\t\t\tif (position == 0) {\n\t\t\t\tswitch ((int) all_id) {\n\t\t\t\t\tcase TaskListFragment.LIST_ID_OVERDUE:\n\t\t\t\t\t\ttitle = getString(R.string.date_header_overdue);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TaskListFragment.LIST_ID_TODAY:\n\t\t\t\t\t\ttitle = getString(R.string.date_header_today);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TaskListFragment.LIST_ID_WEEK:\n\t\t\t\t\t\ttitle = getString(R.string.next_5_days);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TaskListFragment.LIST_ID_ALL:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\ttitle = getString(R.string.all_tasks);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else if (wrappedAdapter != null) {\n\t\t\t\tCursor c = (Cursor) wrappedAdapter.getItem(position - 1);\n\t\t\t\tif (c != null && !c.isAfterLast() && !c.isBeforeFirst()) {\n\t\t\t\t\ttitle = c.getString(1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn title;\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t *\n\t\t * Called when the host view is attempting to determine if an item's\n\t\t * position has changed. Returns POSITION_UNCHANGED if the position of\n\t\t * the given item has not changed or POSITION_NONE if the item is no\n\t\t * longer present in the adapter.\n\t\t *\n\t\t * Argument is the object previously returned by instantiateItem\n\t\t */\n\t\t@Override\n\t\tpublic int getItemPosition(@NonNull Object object) {\n\t\t\tFragment f = (Fragment) object;\n\t\t\tlong listId = f.getArguments().getLong(TaskListFragment.LIST_ID);\n\t\t\treturn getItemPosition(listId);\n\t\t}\n\n\t\t/**\n\t\t * Returns a negative number if id wasn't found in adapter\n\t\t */\n\t\tpublic int getItemPosition(final long listId) {\n\t\t\tint length = getCount();\n\t\t\tint result = POSITION_NONE;\n\t\t\tint position;\n\t\t\tfor (position = 0; position < length; position++) {\n\t\t\t\tif (listId == getItemId(position)) {\n\t\t\t\t\tresult = position;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/interfaces/ListOpener.java",
    "content": "package com.nononsenseapps.notepad.interfaces;\n\nimport com.nononsenseapps.notepad.activities.main.ActivityMain;\nimport com.nononsenseapps.notepad.fragments.TaskListViewPagerFragment;\n\n/**\n * Allows interactions between {@link ActivityMain} and {@link TaskListViewPagerFragment}\n */\npublic interface ListOpener {\n\tvoid openList(final long id);\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/interfaces/MenuStateController.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.interfaces;\n\n/**\n * Used to control the menu items for the navigation drawer.\n */\npublic interface MenuStateController {\n\n\t/**\n\t * If true, menu items should be hidden/removed. Items relevant to the\n\t * navigation drawer should be visible\n\t */\n\tboolean childItemsVisible();\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/interfaces/OnFragmentInteractionListener.java",
    "content": "package com.nononsenseapps.notepad.interfaces;\n\nimport android.net.Uri;\nimport android.view.View;\n\nimport androidx.fragment.app.Fragment;\n\n/**\n * This interface must be implemented by activities that contain\n * fragments to allow an interaction in this fragment to be communicated to\n * the activity and potentially other fragments contained in that activity.\n */\npublic interface OnFragmentInteractionListener {\n\n\tvoid onFragmentInteraction(final Uri uri, final long listId, final View origin);\n\n\tvoid addTaskInList(final String text, final long listId);\n\n\tvoid closeFragment(final Fragment fragment);\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/AboutPrefs.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.prefs;\n\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\n\nimport com.nononsenseapps.notepad.BuildConfig;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.databinding.AppPrefAboutLayoutBinding;\n\npublic class AboutPrefs extends Fragment {\n\n\t/**\n\t * for {@link R.layout#app_pref_about_layout}\n\t */\n\tprivate AppPrefAboutLayoutBinding mBinding;\n\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,\n\t\t\t\t\t\t\t Bundle savInstState) {\n\t\tmBinding = AppPrefAboutLayoutBinding.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\tsuper.onViewCreated(view, savedInstanceState);\n\t\tmBinding.appVersionRow.setText(getString(R.string.version, BuildConfig.VERSION_NAME));\n\t\tmBinding.tvDonations.setText(getString(R.string.app_about_donations,\n\t\t\t\tgetString(R.string.sponsor_this_project),\n\t\t\t\tgetString(R.string.github_repo_url)\n\t\t));\n\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/AppearancePrefs.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.prefs;\n\nimport android.content.Context;\nimport android.os.Bundle;\n\nimport androidx.annotation.Nullable;\nimport androidx.preference.ListPreference;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\n\nimport java.util.ArrayList;\nimport java.util.GregorianCalendar;\nimport java.util.Locale;\n\n/**\n * Settings about how notes will look like: Theme, language, text size, ...\n */\npublic class AppearancePrefs extends PreferenceFragmentCompat {\n\n\tpublic static final String KEY_THEME = \"key_current_theme\";\n\n\tpublic static final String SANS = \"Sans\";\n\tpublic static final String SERIF = \"Serif\";\n\tpublic static final String MONOSPACE = \"Monospace\";\n\n\n\tpublic static final String WEEK_START_DEFAULT = \"-1\";\n\tpublic static final String WEEK_START_SATURDAY = \"7\";\n\tpublic static final String WEEK_START_SUNDAY = \"1\";\n\tpublic static final String WEEK_START_MONDAY = \"2\";\n\n\t@Override\n\tpublic void onCreatePreferences(@Nullable Bundle savInstState, String rootKey) {\n\n\t\t// Load the preferences from an XML resource\n\t\taddPreferencesFromResource(R.xml.app_pref_main);\n\n\t\t// Fill listpreferences\n\t\tsetLangEntries(findPreference(getString(R.string.pref_locale)), getContext());\n\t\tsetDateEntries(findPreference(getString(R.string.key_pref_dateformat_short)), R.array.dateformat_short_values);\n\t\tsetDateEntries(findPreference(getString(R.string.key_pref_dateformat_long)), R.array.dateformat_long_values);\n\n\t\tPrefsActivity\n\t\t\t\t.bindSummaryToValue(findPreference(getString(R.string.key_pref_dateformat_long)));\n\t\tPrefsActivity\n\t\t\t\t.bindSummaryToValue(findPreference(getString(R.string.key_pref_dateformat_short)));\n\t\tPrefsActivity\n\t\t\t\t.bindSummaryToValue(findPreference(getString(R.string.pref_editor_title_fontfamily)));\n\t\tPrefsActivity\n\t\t\t\t.bindSummaryToValue(findPreference(getString(R.string.pref_editor_title_fontstyle)));\n\t\tPrefsActivity\n\t\t\t\t.bindSummaryToValue(findPreference(getString(R.string.pref_editor_body_fontfamily)));\n\t\tPrefsActivity\n\t\t\t\t.bindSummaryToValue(findPreference(getString(R.string.pref_editor_fontsize)));\n\n\t\t// when theme or language changes, restart the PrefsActivity\n\t\tinitializeRestartingPrefWithKey(KEY_THEME);\n\t\tinitializeRestartingPrefWithKey(getString(R.string.pref_locale));\n\t}\n\n\t/**\n\t * Initialize a preference with the given key so that we restart the\n\t * {@link PrefsActivity} when it changes, to see immediately the change.\n\t * Warning: only keys of {@link ListPreference} instances!\n\t */\n\tprivate void initializeRestartingPrefWithKey(String prefKey) {\n\t\tListPreference listPref = findPreference(prefKey);\n\t\t// like \"light_ab\" or \"it_IT\"\n\t\tString prefVal = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(listPref.getContext())\n\t\t\t\t.getString(listPref.getKey(), null);\n\t\tint index = listPref.findIndexOfValue(prefVal);\n\t\t// like \"Light\" or \"italiano\"\n\t\tCharSequence valueToSet = index >= 0 ? listPref.getEntries()[index] : null;\n\t\tlistPref.setSummary(valueToSet);\n\n\t\tlistPref.setOnPreferenceChangeListener((samePref, val) -> {\n\t\t\t// reload activity to apply immediately the new theme or language\n\t\t\tthis.getActivity().recreate();\n\t\t\treturn true;\n\t\t});\n\t}\n\n\tprivate void setDateEntries(ListPreference prefDate, int array) {\n\t\tfinal String[] values = getResources().getStringArray(array);\n\n\t\tfinal ArrayList<CharSequence> entries = new ArrayList<>();\n\n\t\tfinal GregorianCalendar cal = new GregorianCalendar(\n\t\t\t\t2099, 2, 27, 0, 59);\n\n\t\tfor (final String val : values) {\n\t\t\tentries.add(TimeFormatter.getLocalDateString(\n\t\t\t\t\tgetActivity(), val, cal.getTimeInMillis()));\n\t\t}\n\n\t\tprefDate.setEntries(entries.toArray(new CharSequence[0]));\n\t\tprefDate.setEntryValues(values);\n\t}\n\n\tprivate static void setLangEntries(ListPreference prefLang, Context context) {\n\t\tArrayList<CharSequence> entries = new ArrayList<>();\n\t\tArrayList<CharSequence> values = new ArrayList<>();\n\n\t\tentries.add(context.getString(R.string.localedefault));\n\t\tvalues.add(\"\");\n\n\t\tString[] langs = context\n\t\t\t\t.getResources()\n\t\t\t\t.getStringArray(R.array.translated_langs);\n\n\t\tfor (String lang : langs) {\n\t\t\tLocale l;\n\t\t\tif (lang.length() == 5) {\n\t\t\t\tl = new Locale(lang.substring(0, 2), lang.substring(3, 5));\n\t\t\t} else {\n\t\t\t\tl = new Locale(lang.substring(0, 2));\n\t\t\t}\n\n\t\t\tentries.add(l.getDisplayName(l));\n\t\t\tvalues.add(lang);\n\t\t}\n\t\tprefLang.setEntries(entries.toArray(new CharSequence[0]));\n\t\tprefLang.setEntryValues(values.toArray(new CharSequence[0]));\n\n\t\t// Set summary\n\t\tprefLang.setSummary(prefLang.getEntry());\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/BackupPrefs.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.nononsenseapps.notepad.prefs;\n\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.FilePickerHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.fragments.DialogExportBackup;\nimport com.nononsenseapps.notepad.fragments.DialogRestoreBackup;\nimport com.nononsenseapps.notepad.sync.files.JSONBackup;\n\nimport java.io.FileNotFoundException;\nimport java.util.concurrent.Executors;\n\npublic class BackupPrefs extends PreferenceFragmentCompat {\n\n\t// settings IDs from app_pref_backup.xml\n\tprivate static final String KEY_IMPORT = \"backup_import\";\n\tprivate static final String KEY_EXPORT = \"backup_export\";\n\tprivate static final String KEY_BACKUP_DIR_URI = \"key_backup_dir_uri\";\n\n\tprivate JSONBackup mTool;\n\n\t/**\n\t * the folder that contains the backup json file\n\t */\n\tPreference dirUriPref;\n\n\t@Override\n\tpublic void onCreatePreferences(@Nullable Bundle savInstState, String rootKey) {\n\t\t// Load the preferences from an XML resource\n\t\taddPreferencesFromResource(R.xml.app_pref_backup);\n\n\t\tmTool = new JSONBackup(getActivity());\n\n\t\tfindPreference(KEY_IMPORT).setOnPreferenceClickListener(pref -> {\n\t\t\tDialogRestoreBackup.showDialog(getParentFragmentManager(),\n\t\t\t\t\t// callback when confirmed:\n\t\t\t\t\t() -> runBackupOrRestore(true));\n\t\t\treturn true;\n\t\t});\n\n\t\tfindPreference(KEY_EXPORT).setOnPreferenceClickListener(pref -> {\n\t\t\tDialogExportBackup.showDialog(getParentFragmentManager(),\n\t\t\t\t\t() -> runBackupOrRestore(false));\n\t\t\treturn true;\n\t\t});\n\n\t\tdirUriPref = findPreference(KEY_BACKUP_DIR_URI);\n\t\tdirUriPref.setOnPreferenceClickListener(pref -> {\n\t\t\t// open the file picker on click\n\t\t\tUri initialDir = getSelectedBackupDirUri(this.getContext());\n\t\t\tFilePickerHelper.showFolderPickerActivity(this, initialDir);\n\t\t\t// tell android to update the preference value\n\t\t\treturn true;\n\t\t});\n\t\t// initialize\n\t\tonUriDirPrefChange(dirUriPref);\n\t}\n\n\t/**\n\t * Updates the description of \"directoryUriPreference\"\n\t * with the newly selected backup directory Uri\n\t */\n\tprivate static void onUriDirPrefChange(Preference directoryUriPreference) {\n\t\tUri uri = getSelectedBackupDirUri(directoryUriPreference.getContext());\n\t\tString summary = uri != null\n\t\t\t\t? uri.getPath() // shows a pretty representation of the URI's destination\n\t\t\t\t: directoryUriPreference.getContext().getString(R.string.not_selected_yet);\n\t\tdirectoryUriPreference.setSummary(summary);\n\t}\n\n\t/**\n\t * @return the Uri of the folder that the user chose for saving Json backups,\n\t * or NULL if none is chosen\n\t */\n\t@Nullable\n\tpublic static Uri getSelectedBackupDirUri(Context context) {\n\t\tvar sharPrefs = PreferenceManager.getDefaultSharedPreferences(context);\n\t\tString uriVal = sharPrefs.getString(KEY_BACKUP_DIR_URI, null);\n\t\tif (uriVal == null) return null;\n\t\treturn Uri.parse(uriVal);\n\t}\n\n\t@Override\n\tpublic void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {\n\t\t// it was cancelled by the user. Let's ignore it\n\t\tif (resultCode != Activity.RESULT_OK) return;\n\n\t\tif (requestCode == FilePickerHelper.REQ_CODE) {\n\t\t\t// \"data\" contains the URI for the user-selected directory, A.K.A. the \"document tree\"\n\t\t\tFilePickerHelper.onUriPicked(data, this.getContext(), KEY_BACKUP_DIR_URI);\n\t\t\tonUriDirPrefChange(dirUriPref);\n\t\t}\n\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t}\n\n\t/**\n\t * Run the backup (or restore) in the background. Locking the UI-thread for up to a few\n\t * seconds is not nice...\n\t */\n\tprivate void runBackupOrRestore(boolean isRestoring) {\n\t\t// get them in this thread\n\t\tHandler handler = new Handler(Looper.getMainLooper());\n\t\tContext context = this.getContext();\n\n\t\tif (getSelectedBackupDirUri(this.getContext()) == null) {\n\t\t\t// the user tried to make a backup without having selected\n\t\t\t// a folder first. The dialogs warn of this. Here, we just\n\t\t\t// have to cancel the operation\n\t\t\treturn;\n\t\t}\n\n\t\t// replacement for AsyncTask<,,>\n\t\tExecutors.newSingleThreadExecutor().execute(() -> {\n\t\t\t// Background work here\n\t\t\tint result = asyncTask_doInBackground(isRestoring, mTool);\n\n\t\t\thandler.post(() -> {\n\t\t\t\t// UI Thread work here\n\t\t\t\tasyncTask_onPostExecute(context, isRestoring, result);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * the backup/restore work for the background thread\n\t *\n\t * @param isRestoring TRUE if this task should RESTORE a backup from a file,\n\t *                    FALSE if it should CREATE a backup file\n\t * @return a result code used by {@link #asyncTask_onPostExecute(Context, boolean, int)}\n\t */\n\tprivate static int asyncTask_doInBackground(boolean isRestoring, JSONBackup backupMaker) {\n\t\ttry {\n\t\t\tif (isRestoring) backupMaker.restoreBackup();\n\t\t\telse backupMaker.writeBackup();\n\t\t\treturn 0;\n\t\t} catch (FileNotFoundException e) {\n\t\t\treturn 1;\n\t\t} catch (SecurityException e) {\n\t\t\t// can't read from that folder: missing permission ?\n\t\t\treturn 2;\n\t\t} catch (Exception e) {\n\t\t\tNnnLogger.exception(e);\n\t\t\treturn 3;\n\t\t}\n\t}\n\n\t/**\n\t * after the backup/restore is finished, show a toast on the UI thread\n\t *\n\t * @param isRestoring FALSE if it is \"save backup\" operation\n\t * @param result      from {@link #asyncTask_doInBackground(boolean, JSONBackup)}\n\t */\n\tprivate static void asyncTask_onPostExecute(@NonNull Context mContext,\n\t\t\t\t\t\t\t\t\t\t\t\tboolean isRestoring, int result) {\n\t\tint msgId;\n\t\tswitch (result) {\n\t\t\tcase 0 -> msgId = isRestoring\n\t\t\t\t\t? R.string.backup_import_success\n\t\t\t\t\t: R.string.backup_export_success;\n\t\t\tcase 1 -> msgId = R.string.backup_file_not_found;\n\t\t\tcase 2 ->\n\t\t\t\t// can't read from / write to that folder: missing permission ?\n\t\t\t\t\tmsgId = R.string.permission_denied;\n\t\t\tcase 3 -> msgId = isRestoring\n\t\t\t\t\t? R.string.backup_import_failed\n\t\t\t\t\t: R.string.backup_export_failed;\n\t\t\tdefault -> {\n\t\t\t\t// won't happen, anyway\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tToast.makeText(mContext, msgId, Toast.LENGTH_SHORT).show();\n\t}\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/Constants.java",
    "content": "package com.nononsenseapps.notepad.prefs;\n\n/**\n * Key names of the preferences.\n */\npublic final class Constants {\n\n\t// TODO replace calls to SyncPrefs.KEY_SYNC_ENABLE with calls to Constants.KEY_SYNC_ENABLE,\n\t//  which makes more sense. Also ensure that these actually correspond to the values in the\n\t//  XML files\n\n\tpublic static final String KEY_SYNC_ENABLE = \"syncEnablePref\";\n\tpublic static final String KEY_ACCOUNT = \"accountPref\";\n\t// public static final String KEY_SYNC_FREQ = \"syncFreq\";\n\tpublic static final String KEY_FULLSYNC = \"syncFull\";\n\tpublic static final String KEY_SYNC_ON_START = \"syncOnStart\";\n\tpublic static final String KEY_SYNC_ON_CHANGE = \"syncOnChange\";\n\tpublic static final String KEY_BACKGROUND_SYNC = \"syncInBackground\";\n\t// Used for sync on start and on change\n\tpublic static final String KEY_LAST_SYNC = \"lastSync\";\n\t// SD sync\n\tpublic static final String KEY_SD_ENABLE = \"pref_sync_sd_enabled\";\n\n\t// Dropbox sync\n\tpublic static final String KEY_DROPBOX_ENABLE = \"pref_sync_dropbox_enabled\";\n\tpublic static final String KEY_DROPBOX_DIR = \"pref_sync_dropbox_dir\";\n\tpublic static final String KEY_THEME = \"preference_theme\";\n\n\t/**\n\t * Location of the app tutorial web page\n\t */\n\tpublic static final String TUTORIAL_URL =\n\t\t\t\"https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md\";\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/IndexPrefs.java",
    "content": "package com.nononsenseapps.notepad.prefs;\n\n\nimport android.os.Bundle;\n\nimport androidx.annotation.Nullable;\nimport androidx.preference.PreferenceFragmentCompat;\n\nimport com.nononsenseapps.notepad.R;\n\n/**\n * Holds all preference categories, it's the \"main\" settings page\n */\npublic class IndexPrefs extends PreferenceFragmentCompat {\n\n\t@Override\n\tpublic void onCreatePreferences(@Nullable Bundle savInstState, String rootKey) {\n\t\taddPreferencesFromResource(R.xml.app_pref_headers);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/ListPrefs.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.prefs;\n\nimport android.database.Cursor;\nimport android.os.Bundle;\n\nimport androidx.annotation.Nullable;\nimport androidx.preference.ListPreference;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport java.util.ArrayList;\n\npublic class ListPrefs extends PreferenceFragmentCompat {\n\n\t@Override\n\tpublic void onCreatePreferences(@Nullable Bundle savInstState, String rootKey) {\n\t\t// Load the preferences from an XML resource\n\t\taddPreferencesFromResource(R.xml.app_pref_list);\n\n\t\t// Fill listpreferences\n\t\tsetEntries(findPreference(getString(R.string.pref_defaultlist)));\n\n\t\t// Bind summaries\n\t\tPrefsActivity.bindSummaryToValue(findPreference(getString(R.string.pref_sorttype)));\n\t\tPrefsActivity.bindSummaryToValue(findPreference(getString(R.string.pref_defaultlist)));\n\t\tPrefsActivity.bindSummaryToValue(\n\t\t\t\tfindPreference(getString(R.string.pref_list_title_fontfamily)));\n\t\tPrefsActivity.bindSummaryToValue(\n\t\t\t\tfindPreference(getString(R.string.pref_list_title_fontstyle)));\n\t\tPrefsActivity.bindSummaryToValue(\n\t\t\t\tfindPreference(getString(R.string.pref_list_body_fontfamily)));\n\t\tPrefsActivity.bindSummaryToValue(findPreference(getString(R.string.pref_list_fontsize)));\n\n\t\t//PrefsActivity\n\t\t//\t\t.bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_listtype)));\n\n\t\t// Make the show checkbox dependant on the list type preference\n\t\tfinal Preference hideCheckboxes = findPreference(getString(R.string.pref_hidecheckboxes));\n\t\tPreference.OnPreferenceChangeListener listener = (preference, value) -> {\n\t\t\tString stringValue = value.toString();\n\n\t\t\tif (preference instanceof ListPreference listPreference) {\n\t\t\t\t// For list preferences, look up the correct display value in\n\t\t\t\t// the preference's 'entries' list.\n\t\t\t\tint index = listPreference.findIndexOfValue(stringValue);\n\n\t\t\t\t// Set the summary to reflect the new value.\n\t\t\t\tpreference.setSummary(index >= 0 ? listPreference.getEntries()[index] : null);\n\n\t\t\t} else {\n\t\t\t\t// For all other preferences, set the summary to the value's\n\t\t\t\t// simple string representation.\n\t\t\t\tpreference.setSummary(stringValue);\n\t\t\t}\n\n\t\t\thideCheckboxes.setEnabled(stringValue.equals(getString(R.string.const_listtype_tasks)));\n\t\t\treturn true;\n\t\t};\n\t\tfinal Preference listtype = findPreference(getString(R.string.pref_listtype));\n\t\tlisttype.setOnPreferenceChangeListener(listener);\n\t\tlistener.onPreferenceChange(listtype,\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(listtype.getContext())\n\t\t\t\t\t\t.getString(listtype.getKey(), \"\"));\n\t}\n\n\t/**\n\t * Reads the lists from database. Also adds \"All lists\" as the first item.\n\t */\n\tprivate void setEntries(ListPreference listSpinner) {\n\n\t\tArrayList<CharSequence> entries = new ArrayList<>();\n\t\tArrayList<CharSequence> values = new ArrayList<>();\n\n\t\t// TODO fix from old version\n\t\t// listSpinner.setDefaultValue(Long.toString(MainActivity.getAList(getActivity(), -1)));\n\n\t\tCursor cursor = getActivity()\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(TaskList.URI,\n\t\t\t\t\t\tnew String[] { TaskList.Columns._ID, TaskList.Columns.TITLE },\n\t\t\t\t\t\tnull, null, TaskList.Columns.TITLE);\n\t\tif (cursor != null) {\n\t\t\tif (!cursor.isClosed() && !cursor.isAfterLast()) {\n\t\t\t\twhile (cursor.moveToNext()) {\n\t\t\t\t\tentries.add(cursor.getString(1));\n\t\t\t\t\tvalues.add(Long.toString(cursor.getLong(0)));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcursor.close();\n\t\t}\n\n\t\t// Set the values\n\t\tif (listSpinner != null) {\n\t\t\tlistSpinner.setEntries(entries.toArray(new CharSequence[0]));\n\t\t\tlistSpinner.setEntryValues(values.toArray(new CharSequence[0]));\n\n\t\t\tlistSpinner.setSummary(listSpinner.getEntry());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/NotificationPrefs.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.prefs;\n\n\nimport android.app.Activity;\nimport android.app.NotificationManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.media.Ringtone;\nimport android.media.RingtoneManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.PowerManager;\nimport android.provider.Settings;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.content.IntentCompat;\nimport androidx.core.content.PackageManagerCompat;\nimport androidx.core.content.UnusedAppRestrictionsConstants;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceCategory;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.google.common.util.concurrent.ListenableFuture;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.NotificationHelper;\nimport com.nononsenseapps.notepad.BuildConfig;\nimport com.nononsenseapps.notepad.R;\n\npublic class NotificationPrefs extends PreferenceFragmentCompat {\n\n\tprivate static final int REQUEST_CODE_ALERT_RINGTONE = 1;\n\n\t@Override\n\tpublic void onCreatePreferences(@Nullable Bundle savInstState, String rootKey) {\n\n\t\t// Load the preferences from an XML resource\n\t\taddPreferencesFromResource(R.xml.app_pref_notifications);\n\n\t\tPrefsActivity.bindSummaryToValue(\n\t\t\t\tfindPreference(getString(R.string.key_pref_prio)));\n\n\t\t// show the initial value of the selected ringtone\n\t\tupdateRingtonePrefSummary(\n\t\t\t\tfindPreference(getString(R.string.key_pref_ringtone)),\n\t\t\t\tthis.getContext());\n\n\t\t// the \"Preferences for older Android devices\" category\n\t\tPreferenceCategory prefCat = findPreference(getString(R.string.key_pref_cat_notif_old));\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n\t\t\t// newer androids have a dedicated settings page for the notification channel\n\t\t\t// => use that, also because the channel overwrites the individual notifications\n\t\t\tprefCat.setEnabled(false);\n\t\t} else {\n\t\t\t// older androids don't have the notification channel => we keep using our\n\t\t\t// notification preferences => expand their category\n\t\t\tprefCat.setInitialExpandedChildrenCount(Integer.MAX_VALUE);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic boolean onPreferenceTreeClick(Preference preference) {\n\t\tfinal String key = preference.getKey();\n\t\t// if the user clicks a title or a pref. without a key, don't do anything\n\t\tif (key == null) return false;\n\n\t\tfinal String ringtonePrefKey = getString(R.string.key_pref_ringtone);\n\t\tfinal String allowExactRemindersKey = getString(R.string.key_pref_allow_exact_reminders);\n\t\tfinal String ignoreBatteryOptimizationKey = getString(R.string.key_pref_ignore_battery_optimizations);\n\t\tfinal String openNotifChannelKey = getString(R.string.key_pref_notif_channel_settings);\n\t\tfinal String disableHibernation = getString(R.string.key_pref_disable_hibernation);\n\t\tfinal String notificVisibility = getString(R.string.key_pref_notif_visibility);\n\n\t\tif (key.equals(ringtonePrefKey)) {\n\t\t\t// the pseudo-ringtonePreference was clicked => open a system page to pick a ringtone\n\t\t\tIntent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER)\n\t\t\t\t\t.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,\n\t\t\t\t\t\t\tRingtoneManager.TYPE_NOTIFICATION)\n\t\t\t\t\t.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)\n\t\t\t\t\t.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true)\n\t\t\t\t\t.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,\n\t\t\t\t\t\t\tSettings.System.DEFAULT_NOTIFICATION_URI);\n\n\t\t\tString existingValue = PreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(this.getContext())\n\t\t\t\t\t.getString(ringtonePrefKey, null);\n\t\t\tif (existingValue != null) {\n\t\t\t\tUri existing = existingValue.isEmpty()\n\t\t\t\t\t\t? null // Select \"Silent\"\n\t\t\t\t\t\t: Uri.parse(existingValue);\n\t\t\t\tintent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existing);\n\t\t\t} else {\n\t\t\t\t// No ringtone has been selected, set to the default\n\t\t\t\tintent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,\n\t\t\t\t\t\tSettings.System.DEFAULT_NOTIFICATION_URI);\n\t\t\t}\n\n\t\t\tstartActivityForResult(intent, REQUEST_CODE_ALERT_RINGTONE);\n\t\t\treturn true;\n\t\t} else if (key.equals(allowExactRemindersKey)) {\n\t\t\tif (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {\n\t\t\t\t// open a settings page to enable exact reminders for this app.\n\t\t\t\t// they're enabled by default in the Android 12 emulator\n\t\t\t\tIntent i = new Intent()\n\t\t\t\t\t\t.setAction(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)\n\t\t\t\t\t\t.setData(Uri.parse(\"package:\" + getContext().getPackageName()));\n\t\t\t\tstartActivity(i);\n\t\t\t} else {\n\t\t\t\t// not needed before android S\n\t\t\t}\n\t\t\t// we don't care about the value\n\t\t\treturn false;\n\t\t} else if (key.equals(ignoreBatteryOptimizationKey)) {\n\t\t\t// open the battery settings when clicked\n\t\t\tIntent i = new Intent()\n\t\t\t\t\t.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);\n\t\t\tstartActivity(i);\n\n\t\t\t// the value of this preference is never used,\n\t\t\t// it's just something the user can click to open a settings page\n\t\t\treturn false;\n\t\t} else if (key.equals(openNotifChannelKey)) {\n\t\t\topenNotificationSettings(this.getContext());\n\t\t\treturn false;\n\t\t} else if (key.equals(notificVisibility)) {\n\t\t\t// open the app settings to let the user change app permissions\n\t\t\tIntent i = new Intent(\n\t\t\t\t\tandroid.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,\n\t\t\t\t\tUri.parse(\"package:\" + BuildConfig.APPLICATION_ID));\n\t\t\tstartActivity(i);\n\t\t\treturn false;\n\t\t} else if (key.equals(disableHibernation)) {\n\t\t\tshowHibernationPageIfNeeded(this);\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn super.onPreferenceTreeClick(preference);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void onActivityResult(int requestCode, int resultCode, Intent data) {\n\n\t\tif (resultCode != Activity.RESULT_OK || data == null) {\n\t\t\t// canceled by the user\n\t\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t\t\treturn;\n\t\t}\n\n\t\tif (requestCode == REQUEST_CODE_ALERT_RINGTONE) {\n\t\t\t// the user picked a ringtone => save it\n\t\t\tUri ringtone = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);\n\t\t\tString ringtonePrefKey = getString(R.string.key_pref_ringtone);\n\t\t\tPreference pref = findPreference(ringtonePrefKey);\n\n\t\t\t// ringtone == null means that \"Silent\" was selected in the picker\n\t\t\tString newPrefVal = ringtone == null ? null : ringtone.toString();\n\n\t\t\t// save the new value\n\t\t\tPreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(this.getContext())\n\t\t\t\t\t.edit()\n\t\t\t\t\t.putString(ringtonePrefKey, newPrefVal)\n\t\t\t\t\t.commit();\n\t\t\t// show it\n\t\t\tupdateRingtonePrefSummary(pref, this.getContext());\n\t\t} else {\n\t\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t\t}\n\t}\n\n\t/**\n\t * Get the ringtone name and show it in the summary\n\t *\n\t * @param theRingtonePref a reference to the {@link Preference} object of the ringtone\n\t */\n\tprivate static void updateRingtonePrefSummary(Preference theRingtonePref, Context con) {\n\n\t\t// get the URI saved in the preferences\n\t\tString ringtonePrefKey = con.getString(R.string.key_pref_ringtone);\n\t\tfinal String ringtonePrefVal = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(con)\n\t\t\t\t.getString(ringtonePrefKey, null);\n\t\tfinal Uri newVal = ringtonePrefVal == null ? null : Uri.parse(ringtonePrefVal);\n\n\t\t// look up the correct display value using RingtoneManager\n\t\tif (newVal == null) {\n\t\t\t// Empty values correspond to 'silent' (no ringtone)\n\t\t\ttheRingtonePref.setSummary(R.string.silent);\n\t\t} else {\n\t\t\tRingtone ringtone = RingtoneManager.getRingtone(con, newVal);\n\t\t\tif (ringtone == null) {\n\t\t\t\t// Clear the summary if there was a lookup error\n\t\t\t\ttheRingtonePref.setSummary(null);\n\t\t\t} else {\n\t\t\t\t// Set the summary to reflect the new ringtone display name\n\t\t\t\tString name = ringtone.getTitle(con);\n\t\t\t\ttheRingtonePref.setSummary(name);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onResume() {\n\t\tsuper.onResume();\n\n\t\t// check if battery optimizations are enabled and show it in the summary\n\t\tvar pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);\n\t\tint summaryResId1 = pm.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)\n\t\t\t\t? R.string.battery_optimizations_inactive\n\t\t\t\t: R.string.battery_optimizations_active;\n\t\tfindPreference(getString(R.string.key_pref_ignore_battery_optimizations))\n\t\t\t\t.setSummary(summaryResId1);\n\n\t\tvar nm = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);\n\t\tint summaryResId2 = NotificationHelper.areNotificationsVisible(nm)\n\t\t\t\t? R.string.notifications_enabled\n\t\t\t\t: R.string.notifications_blocked;\n\t\tfindPreference(getString(R.string.key_pref_notif_visibility))\n\t\t\t\t.setSummary(summaryResId2);\n\t}\n\n\t/**\n\t * opens a system settings page dedicated to notification preferences for <br/>\n\t * - our only notification channel (only devices on Oreo or newer) <br/>\n\t * - the app as a whole (only devices on API 23, 24 or 25) <br/>\n\t * In android Oreo and newer, these settings overwrite those of the old preferences,\n\t * which now are in the {@link PreferenceCategory} \"key_pref_cat_notif_old\"\n\t */\n\tprivate static void openNotificationSettings(Context context) {\n\t\tIntent intent;\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n\t\t\tintent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)\n\t\t\t\t\t.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName())\n\t\t\t\t\t.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationHelper.CHANNEL_ID);\n\t\t} else {\n\t\t\t// it works on a tablet with API 23. But it's not as complete as the\n\t\t\t// notification channel preference page on API 32 devices, for example\n\t\t\tintent = new Intent(\"android.settings.APP_NOTIFICATION_SETTINGS\")\n\t\t\t\t\t.putExtra(\"app_package\", context.getPackageName())\n\t\t\t\t\t.putExtra(\"app_uid\", context.getApplicationInfo().uid);\n\t\t}\n\t\tcontext.startActivity(intent);\n\t}\n\n\t// TODO test the app in doze mode: see\n\t//  https://developer.android.com/training/monitoring-device-state/doze-standby#testing_doze\n\t//  command: $ adb shell dumpsys alarm\n\t//  in particular, ensure that the notification arrive at a reasonable time\n\n\n\t/**\n\t * If the user doesn't start the app for a few months, the system will\n\t * place restrictions on it. See the {@link UnusedAppRestrictionsConstants} for details.\n\t * This function shows the settings page where the user can disable this behavior\n\t */\n\tstatic void showHibernationPageIfNeeded(@NonNull PreferenceFragmentCompat owner) {\n\t\tvar context = owner.getContext();\n\t\tListenableFuture<Integer> lfi = PackageManagerCompat\n\t\t\t\t.getUnusedAppRestrictionsStatus(context);\n\t\tlfi.addListener(() -> {\n\t\t\t// if we're going to show the settings page to disable hibernation\n\t\t\tboolean showPage;\n\t\t\ttry {\n\t\t\t\tint appRestrictionsStatus = lfi.get();\n\t\t\t\tswitch (appRestrictionsStatus) {\n\t\t\t\t\tcase UnusedAppRestrictionsConstants.API_30_BACKPORT:\n\t\t\t\t\tcase UnusedAppRestrictionsConstants.API_30:\n\t\t\t\t\tcase UnusedAppRestrictionsConstants.API_31:\n\t\t\t\t\t\t// restriction enabled => show settings page to let users disable it\n\t\t\t\t\t\tshowPage = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase UnusedAppRestrictionsConstants.ERROR:\n\t\t\t\t\tcase UnusedAppRestrictionsConstants.FEATURE_NOT_AVAILABLE:\n\t\t\t\t\tcase UnusedAppRestrictionsConstants.DISABLED:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// restriction not enabled => don't show settings page\n\t\t\t\t\t\tshowPage = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tNnnLogger.exception(ex);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (showPage) {\n\t\t\t\t// ask the user to disable these restrictions: redirect the user to\n\t\t\t\t// the page in system settings to disable the feature.\n\t\t\t\tString pkgName = context.getPackageName();\n\t\t\t\tIntent i = IntentCompat.createManageUnusedAppRestrictionsIntent(context, pkgName);\n\n\t\t\t\t// You must use startActivityForResult(), not startActivity(), even if\n\t\t\t\t// you don't use the result code returned in onActivityResult().\n\t\t\t\towner.startActivityForResult(i, 12345);\n\t\t\t} else {\n\t\t\t\t// tell the user that hibernation is already OFF\n\t\t\t\tToast.makeText(context, R.string.msg_hibernation_already_off, Toast.LENGTH_SHORT)\n\t\t\t\t\t\t.show();\n\t\t\t}\n\t\t}, ContextCompat.getMainExecutor(context));\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/PasswordPrefs.java",
    "content": "/*\n * Copyright (C) 2012 Jonas Kalderstam\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nononsenseapps.notepad.prefs;\n\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.Animation;\nimport android.view.animation.AnimationUtils;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.databinding.AppPrefPasswordLayoutBinding;\nimport com.nononsenseapps.notepad.fragments.DialogPasswordV11;\n\npublic class PasswordPrefs extends Fragment {\n\n\tpublic static final String KEY_PASSWORD = \"secretPassword\";\n\n\t// TODO copy from DialogPasswordSettings.java and delete that file\n\n\t/**\n\t * for {@link R.layout#app_pref_password_layout}\n\t */\n\tprivate AppPrefPasswordLayoutBinding mBinding;\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(@NonNull LayoutInflater inflater,\n\t\t\t\t\t\t\t @Nullable ViewGroup container,\n\t\t\t\t\t\t\t @Nullable Bundle savedInstanceState) {\n\t\tmBinding = AppPrefPasswordLayoutBinding\n\t\t\t\t.inflate(inflater, container, false);\n\t\treturn mBinding.getRoot();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n\t\t// here you call methods with the old @AfterViews annotation\n\t\tmBinding.applyPassword.setOnClickListener(v -> applyPassword());\n\t\tmBinding.clearPassword.setOnClickListener(v -> clearPassword());\n\t}\n\n\tprivate void applyPassword() {\n\t\tString passw1 = mBinding.tempPassword1.getText().toString();\n\t\tString passw2 = mBinding.tempPassword2.getText().toString();\n\t\tif (passw1.equals(passw2)) {\n\t\t\t// They are the same\n\t\t\tSharedPreferences settings = PreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(this.getContext());\n\t\t\tString currentPassword = settings.getString(KEY_PASSWORD, \"\");\n\n\t\t\tif (currentPassword.isEmpty()) {\n\t\t\t\t// it's new => Save the password directly\n\t\t\t\tsettings.edit()\n\t\t\t\t\t\t.putString(KEY_PASSWORD, passw1)\n\t\t\t\t\t\t.commit();\n\t\t\t\tToast.makeText(this.getContext(), getText(R.string.password_set),\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t} else {\n\t\t\t\t// confirm with existing password first\n\t\t\t\tshowPasswordDialog(passw1);\n\t\t\t}\n\t\t} else {\n\t\t\tif (PreferencesHelper.areAnimationsEnabled(this.getContext())) {\n\t\t\t\t// shake the dialog to show that the password is wrong\n\t\t\t\tAnimation shake = AnimationUtils.loadAnimation(this.getContext(), R.anim.shake);\n\t\t\t\tmBinding.tempPassword2.startAnimation(shake);\n\t\t\t}\n\t\t\t// Show a toast so the user knows he did something wrong\n\t\t\tToast.makeText(this.getContext(), getText(R.string.passwords_dont_match),\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t}\n\t}\n\n\tprivate void clearPassword() {\n\t\tSharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(this.getContext());\n\t\tString currentPassword = settings.getString(KEY_PASSWORD, \"\");\n\n\t\tif (currentPassword.isEmpty()) {\n\t\t\t// Save the (empty) password directly\n\t\t\tsettings.edit()\n\t\t\t\t\t.putString(KEY_PASSWORD, \"\")\n\t\t\t\t\t.commit();\n\t\t\tToast.makeText(this.getContext(), R.string.password_cleared,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t} else {\n\t\t\t// confirm with existing password first\n\t\t\tshowPasswordDialog(\"\");\n\t\t}\n\t}\n\n\tprivate void showPasswordDialog(final String newPassword) {\n\t\tfinal DialogPasswordV11 pd = new DialogPasswordV11();\n\t\tpd.setListener(() -> {\n\t\t\tPreferenceManager\n\t\t\t\t\t.getDefaultSharedPreferences(this.getContext())\n\t\t\t\t\t.edit()\n\t\t\t\t\t.putString(PasswordPrefs.KEY_PASSWORD, newPassword)\n\t\t\t\t\t.commit();\n\t\t\tToast.makeText(getActivity(),\n\t\t\t\t\t\"\".equals(newPassword) ? R.string.password_cleared : R.string.password_set,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t});\n\t\tpd.show(getParentFragmentManager(), \"pw-verify\");\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/PrefsActivity.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.prefs;\n\nimport android.app.backup.BackupManager;\nimport android.os.Bundle;\nimport android.view.MenuItem;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentContainerView;\nimport androidx.preference.ListPreference;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.ActivityHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.NotificationHelper;\nimport com.nononsenseapps.helpers.ThemeHelper;\nimport com.nononsenseapps.notepad.R;\n\n/**\n * The preferences page, holds a list of all preference categories\n */\npublic class PrefsActivity extends AppCompatActivity implements\n\t\tPreferenceFragmentCompat.OnPreferenceStartFragmentCallback {\n\n\tprivate boolean isTabletInLandscape = false;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tThemeHelper.setTheme(this);\n\t\tActivityHelper.setSelectedLanguage(this);\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\t// Add the arrow to go back\n\t\tif (getSupportActionBar() != null) {\n\t\t\tgetSupportActionBar().setDisplayHomeAsUpEnabled(true);\n\t\t\t// the title updates when the user chooses a new language setting,\n\t\t\t// but ONLY if we set it here\n\t\t\tgetSupportActionBar().setTitle(R.string.menu_preferences);\n\t\t}\n\n\t\t// inflates a layout with a fragmentcontainerview, which will\n\t\t// automatically start an instance of IndexPrefs\n\t\tsetContentView(R.layout.activity_settings);\n\n\t\t// this exists only in the tablet-landscape layout file\n\t\tFragmentContainerView fragmentSpot2 = this.findViewById(R.id.fragmentRightForTablets);\n\t\tisTabletInLandscape = fragmentSpot2 != null;\n\n\t\tgetSupportFragmentManager().addOnBackStackChangedListener(() -> {\n\t\t\tint numActiveFrags = getSupportFragmentManager().getBackStackEntryCount();\n\t\t\tif (numActiveFrags == 1) {\n\t\t\t\t// it's opening a settings category => there is nothing to do\n\t\t\t} else if (numActiveFrags == 0) {\n\t\t\t\t// it's going back to the \"main menu\" => remove the subtitle\n\t\t\t\tif (getSupportActionBar() != null) {\n\t\t\t\t\tgetSupportActionBar().setSubtitle(null);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tNnnLogger.warning(PrefsActivity.class,\n\t\t\t\t\t\t\"unexpected numActiveFrags = \" + numActiveFrags);\n\t\t\t}\n\t\t});\n\n\t\t// when pressing the physical back button, navigate between fragments by removing the\n\t\t// subtitle\n\t\tgetOnBackPressedDispatcher().addCallback(this,\n\t\t\t\tnew OnBackPressedCallback(true) {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void handleOnBackPressed() {\n\t\t\t\t\t\t// replicate super.onBackPressed() behavior\n\t\t\t\t\t\tif (getSupportFragmentManager().getBackStackEntryCount() > 0) {\n\t\t\t\t\t\t\tgetSupportFragmentManager().popBackStack();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfinish();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\t/**\n\t * called when a settings category is clicked. It opens the appropriate\n\t * preference fragment. From:\n\t * <a href=\"https://developer.android.com/develop/ui/views/components/settings/organize-your-settings\">here</a>.\n\t */\n\t@Override\n\tpublic boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat caller,\n\t\t\t\t\t\t\t\t\t\t\t Preference pref) {\n\t\t// Instantiate the new Fragment\n\t\tfinal Bundle args = pref.getExtras();\n\t\tfinal Fragment fragment = getSupportFragmentManager()\n\t\t\t\t.getFragmentFactory()\n\t\t\t\t.instantiate(getClassLoader(), pref.getFragment());\n\t\tfragment.setArguments(args);\n\t\tfragment.setTargetFragment(caller, 0);\n\n\t\tif (isTabletInLandscape) {\n\t\t\t// for tablets in landscape mode, 2 fragments are shown (=> 2 pane view):\n\t\t\t// the \"main menu\" remains on the left, the preference page list opens on the right\n\t\t\tgetSupportFragmentManager()\n\t\t\t\t\t.beginTransaction()\n\t\t\t\t\t.replace(R.id.fragmentRightForTablets, fragment)\n\t\t\t\t\t// don't call .addToBackStack(null), so the back button will immediately exit\n\t\t\t\t\t.commit();\n\n\t\t} else {\n\t\t\t// for phones & tablets in portrait mode, there is only 1 fragment shown:\n\t\t\t// Replace the existing \"main menu\" Fragment with the new \"category\" Fragment\n\t\t\tgetSupportFragmentManager()\n\t\t\t\t\t.beginTransaction()\n\t\t\t\t\t.replace(R.id.fragment, fragment)\n\t\t\t\t\t.addToBackStack(null)\n\t\t\t\t\t.commit();\n\t\t}\n\n\t\tif (getSupportActionBar() != null) {\n\t\t\tgetSupportActionBar().setSubtitle(pref.getTitle());\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\t// Request a backup in case prefs changed. Safe to call multiple times\n\t\tnew BackupManager(this).dataChanged();\n\t\t// show reminders notifications. Useful when the user re-enables notifications\n\t\t// permissions: in this case, any overdue notification should be shown immediately\n\t\tNotificationHelper.schedule(this);\n\t\tsuper.onDestroy();\n\t}\n\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tif (item.getItemId() == android.R.id.home) {\n\t\t\t// This ID represents the Home or Up button. In this activity, the Up button is shown.\n\t\t\t// To get a consistent behavior, both pressing \"back\" and clicking the Up arrow\n\t\t\t// will navigate back, so if a preference category is shown, pressing the Up\n\t\t\t// button won't close the settings, it will go back to the Index\n\t\t\tgetOnBackPressedDispatcher().onBackPressed();\n\t\t\treturn true;\n\t\t}\n\t\treturn super.onOptionsItemSelected(item);\n\t}\n\n\t/**\n\t * A preference value change listener that updates the preference's summary\n\t * to reflect its new value. Handles the {@link ListPreference} specially.\n\t */\n\tprivate static final Preference.OnPreferenceChangeListener\n\t\t\tsBindPreferenceSummaryToValueListener = (preference, value) -> {\n\t\tfinal String stringValue = value.toString();\n\n\t\tif (preference instanceof ListPreference listPreference) {\n\t\t\t// For list preferences, look up the correct display value in\n\t\t\t// the preference's 'entries' list.\n\t\t\tint index = listPreference.findIndexOfValue(stringValue);\n\n\t\t\t// Set the summary to reflect the new value, if possible\n\t\t\tpreference.setSummary(index >= 0 ? listPreference.getEntries()[index] : null);\n\n\t\t} else {\n\t\t\t// For all other preferences, set the summary to the value's\n\t\t\t// simple string representation.\n\t\t\tpreference.setSummary(stringValue);\n\t\t}\n\t\treturn true;\n\t};\n\n\t/**\n\t * Binds a preference's summary to its value. When the preference's value is changed,\n\t * its summary (text below the preference title) is updated to reflect the value.\n\t * The summary is also updated upon calling this method. The exact display format is\n\t * dependent on the type of preference.\n\t *\n\t * @see #sBindPreferenceSummaryToValueListener\n\t */\n\tpublic static void bindSummaryToValue(Preference preference) {\n\t\t// Set the listener to watch for value changes.\n\t\tpreference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);\n\n\t\t// Trigger the listener immediately with the preference's current value.\n\t\tsBindPreferenceSummaryToValueListener.onPreferenceChange(preference,\n\t\t\t\tPreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(preference.getContext())\n\t\t\t\t\t\t.getString(preference.getKey(), \"\"));\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/SyncPrefs.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.prefs;\n\nimport android.accounts.Account;\nimport android.accounts.AccountManagerFuture;\nimport android.app.Activity;\nimport android.content.ContentResolver;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.os.Bundle;\nimport android.widget.Toast;\n\nimport androidx.annotation.Nullable;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.FileHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.MyContentProvider;\nimport com.nononsenseapps.notepad.sync.orgsync.OrgSyncService;\n\npublic class SyncPrefs extends PreferenceFragmentCompat\n\t\timplements OnSharedPreferenceChangeListener {\n\n\t/**\n\t * Used for sync on start and on change\n\t */\n\tpublic static final String KEY_LAST_SYNC = \"lastSync\";\n\tprivate static final int PICK_ACCOUNT_CODE = 2;\n\n\t// SD sync\n\tpublic static final String KEY_SD_ENABLE = \"pref_sync_sd_enabled\";\n\tpublic static final String KEY_SD_SYNC_INFO = \"pref_sdcard_sync_info\";\n\n\t@Override\n\tpublic void onCreatePreferences(@Nullable Bundle savInstState, String rootKey) {\n\n\t\t// Load the preferences from an XML resource\n\t\taddPreferencesFromResource(R.xml.app_pref_sync);\n\n\t\tfinal SharedPreferences sharedPrefs = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(this.getContext());\n\t\t// Set up a listener whenever a key changes\n\t\tsharedPrefs.registerOnSharedPreferenceChangeListener(this);\n\n\t\tfindPreference(KEY_SD_ENABLE).setOnPreferenceClickListener(p -> {\n\t\t\t// if the ORG dir is inaccessible, disable SD sync\n\t\t\tString dir = FileHelper.getUserSelectedOrgDir(this.getContext());\n\t\t\tif (dir == null) {\n\t\t\t\tPreferencesHelper.disableSdCardSync(this.getContext());\n\t\t\t\tNnnLogger.warning(SyncPrefs.class, \"Can't access org dir\");\n\t\t\t\treturn false;\n\t\t\t} else\n\t\t\t\treturn true;\n\t\t});\n\n\t\t// write the folder path on the summary\n\t\tString orgdirpath = FileHelper.getUserSelectedOrgDir(this.getContext());\n\t\tString sdInfoSummary = this.getString(R.string.directory_summary_msg, orgdirpath);\n\t\tfindPreference(KEY_SD_SYNC_INFO).setSummary(sdInfoSummary);\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\tsuper.onDestroy();\n\t\tPreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(this.getContext())\n\t\t\t\t.unregisterOnSharedPreferenceChangeListener(this);\n\t}\n\n\t/**\n\t * Called when a shared preference is changed, added, or removed. This\n\t * may be called even if a preference is set to its existing value.\n\t * <p/>\n\t * <p>This callback will be run on your main thread.\n\t *\n\t * @param prefs The {@link SharedPreferences} that received the change.\n\t * @param key   The key of the preference that was changed, added, or removed\n\t */\n\tpublic void onSharedPreferenceChanged(SharedPreferences prefs, String key) {\n\t\tNnnLogger.debug(SyncPrefs.class, \"onChanged\");\n\t\tfinal String keySyncMaster = this.getString(R.string.key_pref_sync_enabled_master);\n\t\ttry {\n\n\t\t\tif (this.getActivity().isFinishing()) {\n\t\t\t\t// Setting the summary now would crash it with\n\t\t\t\t// IllegalStateException since we are not attached to a view\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// => now we can safely continue\n\t\t\tif (KEY_SD_ENABLE.equals(key)) {\n\t\t\t\t// Restart the sync service\n\t\t\t\tOrgSyncService.stop(getActivity());\n\t\t\t} else if (keySyncMaster.equals(key)) {\n\t\t\t\t// TODO force stop / re-enable all (user selected) sync services\n\t\t\t}\n\t\t} catch (IllegalStateException e) {\n\t\t\t// This is just in case the \"isFinishing\" wouldn't be enough\n\t\t\t// The isFinishing will try to prevent us from doing something stupid\n\t\t\t// This catch prevents the app from crashing if we do something stupid\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onActivityResult(int requestCode, int resultCode, Intent data) {\n\t\tif (resultCode != Activity.RESULT_OK) {\n\t\t\t// it was cancelled by the user. Let's ignore it in both cases\n\t\t\treturn;\n\t\t}\n\t\tif (requestCode == PICK_ACCOUNT_CODE) {\n\t\t\t// the user has confirmed with a valid account on the account picker\n\t\t\t// String chosenAccountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);\n\t\t\t// then make and call something like userChoseAnAccountWithName(chosenAccountName);\n\t\t}\n\n\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t}\n\n\t/**\n\t * Called when the user has selected an account when pressing the enable sync\n\t * switch. User wants to select an account to sync with. If we get an approval,\n\t * activate sync and set periodicity also.\n\t */\n\tprivate void afterGettingAuthToken(AccountManagerFuture<Bundle> future, Account account) {\n\t\ttry {\n\t\t\tNnnLogger.debug(SyncPrefs.class, \"step two\");\n\n\n\t\t\tif (account != null) {\n\n\t\t\t\t// Also mark enabled as true, as the dialog was shown from enable button\n\t\t\t\tNnnLogger.debug(SyncPrefs.class, \"step three: \" + account.name);\n\n\t\t\t\tSharedPreferences customSharedPreference = PreferenceManager\n\t\t\t\t\t\t.getDefaultSharedPreferences(this.getContext());\n\t\t\t\tcustomSharedPreference\n\t\t\t\t\t\t.edit()\n\t\t\t\t\t\t.putString(\"pref_key_for_the_account\", account.name)\n\t\t\t\t\t\t.putBoolean(\"pref_to_enable_this_sync\", true)\n\t\t\t\t\t\t.commit();\n\n\t\t\t\t// Set it syncable\n\t\t\t\tContentResolver\n\t\t\t\t\t\t.setSyncAutomatically(account, MyContentProvider.AUTHORITY, true);\n\t\t\t\tContentResolver\n\t\t\t\t\t\t.setIsSyncable(account, MyContentProvider.AUTHORITY, 1);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\t// OperationCanceledException:\n\t\t\t// * if the request was canceled for any reason\n\t\t\t// AuthenticatorException:\n\t\t\t// * if there was an error communicating with the authenticator or\n\t\t\t// * if the authenticator returned an invalid response or\n\t\t\t// * if the user did not register on the api console\n\t\t\t// IOException:\n\t\t\t// * if the authenticator returned an error response that\n\t\t\t// * indicates that it encountered an IOException while\n\t\t\t// * communicating with the authentication server\n\t\t\tString errMsg = e.getClass().getSimpleName() + \": \" + e.getMessage();\n\t\t\tToast.makeText(this.getContext(), errMsg, Toast.LENGTH_SHORT).show();\n\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/SyncAdapter.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync;\n\n/**\n * this used to do google tasks sync. Now it only provides constants\n */\npublic final class SyncAdapter {\n\n\tprivate SyncAdapter() {}\n\n\tpublic static final String SYNC_STARTED = \"com.nononsenseapps.notepad.sync.SYNC_STARTED\";\n\tpublic static final String SYNC_FINISHED = \"com.nononsenseapps.notepad.sync.SYNC_FINISHED\";\n\n\tpublic static final String SYNC_RESULT = \"com.nononsenseapps.notepad.sync.SYNC_RESULT\";\n\tpublic static final int SUCCESS = 0;\n\tpublic static final int LOGIN_FAIL = 1;\n\tpublic static final int ERROR = 2;\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/files/JSONBackup.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.files;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.util.Log;\n\nimport com.nononsenseapps.helpers.DocumentFileHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.NotificationHelper;\nimport com.nononsenseapps.notepad.database.Notification;\nimport com.nononsenseapps.notepad.database.RemoteTask;\nimport com.nononsenseapps.notepad.database.RemoteTaskList;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.prefs.BackupPrefs;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class JSONBackup {\n\n\tprivate static final String KEY_REMINDERS = \"reminders\";\n\tprivate static final String KEY_TASKS = \"tasks\";\n\tprivate static final String KEY_REMOTES = \"remotes\";\n\tprivate static final String KEY_LISTS = \"lists\";\n\n\tprivate final Context context;\n\n\tpublic JSONBackup(final Context context) {\n\t\tthis.context = context;\n\t}\n\n\n\tprivate List<TaskList> getTaskLists() {\n\t\tfinal ArrayList<TaskList> taskLists = new ArrayList<>();\n\n\t\tfinal Cursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(TaskList.URI, TaskList.Columns.FIELDS,\n\t\t\t\t\t\tnull, null, TaskList.Columns.TITLE);\n\n\t\twhile (c != null && c.moveToNext()) {\n\t\t\ttaskLists.add(new TaskList(c));\n\t\t}\n\n\t\tif (c != null)\n\t\t\tc.close();\n\n\t\treturn taskLists;\n\t}\n\n\tprivate List<RemoteTaskList> getRemotesOf(final TaskList list) {\n\t\tfinal ArrayList<RemoteTaskList> remotes = new ArrayList<>();\n\n\t\tfinal Cursor c = context.getContentResolver().query(RemoteTaskList.URI,\n\t\t\t\tRemoteTaskList.Columns.FIELDS,\n\t\t\t\tRemoteTaskList.Columns.DBID + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(list._id) },\n\t\t\t\tRemoteTaskList.Columns.SERVICE);\n\n\t\twhile (c != null && c.moveToNext()) {\n\t\t\tremotes.add(new RemoteTaskList(c));\n\t\t}\n\n\t\tif (c != null)\n\t\t\tc.close();\n\n\t\treturn remotes;\n\t}\n\n\tprivate List<Task> getTasksIn(final TaskList list) {\n\t\tfinal ArrayList<Task> tasks = new ArrayList<>();\n\n\t\t// Reverse order because adding stuff is always done at the top\n\t\tfinal Cursor c = context.getContentResolver().query(Task.URI,\n\t\t\t\tTask.Columns.FIELDS, Task.Columns.DBLIST + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(list._id) },\n\t\t\t\tTask.Columns.LEFT + \" DESC\");\n\n\t\twhile (c != null && c.moveToNext()) {\n\t\t\ttasks.add(new Task(c));\n\t\t}\n\n\t\tif (c != null)\n\t\t\tc.close();\n\n\t\treturn tasks;\n\t}\n\n\tprivate List<RemoteTask> getRemotesOf(final Task task) {\n\t\tfinal ArrayList<RemoteTask> remotes = new ArrayList<>();\n\n\t\tfinal Cursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(RemoteTask.URI,\n\t\t\t\t\t\tRemoteTask.Columns.FIELDS,\n\t\t\t\t\t\tRemoteTask.Columns.DBID + \" IS ?\",\n\t\t\t\t\t\tnew String[] { Long.toString(task._id) },\n\t\t\t\t\t\tRemoteTask.Columns.SERVICE);\n\n\t\twhile (c != null && c.moveToNext()) {\n\t\t\tremotes.add(new RemoteTask(c));\n\t\t}\n\n\t\tif (c != null)\n\t\t\tc.close();\n\n\t\treturn remotes;\n\t}\n\n\tprivate List<Notification> getRemindersFor(final Task task) {\n\t\tfinal ArrayList<Notification> reminders = new ArrayList<>();\n\n\t\tfinal Cursor c = context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(Notification.URI,\n\t\t\t\t\t\tNotification.Columns.FIELDS,\n\t\t\t\t\t\tNotification.Columns.TASKID + \" IS ?\",\n\t\t\t\t\t\tnew String[] { Long.toString(task._id) },\n\t\t\t\t\t\tNotification.Columns.TIME);\n\n\t\twhile (c != null && c.moveToNext()) {\n\t\t\treminders.add(new Notification(c));\n\t\t}\n\n\t\tif (c != null)\n\t\t\tc.close();\n\n\t\treturn reminders;\n\t}\n\n\tprivate JSONObject getJSONBackup() throws JSONException {\n\t\tfinal JSONArray listarray = new JSONArray();\n\t\tfor (final TaskList list : getTaskLists()) {\n\t\t\tfinal JSONObject jsonlist = new JSONObject();\n\t\t\tjsonlist.put(TaskList.Columns._ID, list._id);\n\t\t\taddAllContentToJSON(list.getContent(), jsonlist);\n\n\t\t\tjsonlist.put(KEY_REMOTES, getJSONRemotesFor(list));\n\t\t\tjsonlist.put(KEY_TASKS, getJSONTasksFor(list));\n\n\t\t\t// Add tasklist to array\n\t\t\tlistarray.put(jsonlist);\n\t\t}\n\t\tfinal JSONObject backup = new JSONObject();\n\t\tbackup.put(KEY_LISTS, listarray);\n\t\treturn backup;\n\t}\n\n\tprivate void addAllContentToJSON(final ContentValues content,\n\t\t\t\t\t\t\t\t\t final JSONObject json) throws JSONException {\n\t\tfor (String key : content.keySet()) {\n\t\t\tjson.put(key, content.get(key));\n\t\t}\n\t}\n\n\tprivate JSONArray getJSONRemotesFor(final TaskList list)\n\t\t\tthrows JSONException {\n\t\tfinal JSONArray remotelistarray = new JSONArray();\n\t\tfor (final RemoteTaskList remote : getRemotesOf(list)) {\n\t\t\tfinal JSONObject jsonremote = new JSONObject();\n\t\t\tjsonremote.put(RemoteTaskList.Columns._ID, remote._id);\n\t\t\taddAllContentToJSON(remote.getContent(), jsonremote);\n\n\t\t\tremotelistarray.put(jsonremote);\n\t\t}\n\t\treturn remotelistarray;\n\t}\n\n\tprivate JSONArray getJSONTasksFor(final TaskList list) throws JSONException {\n\t\tfinal JSONArray taskarray = new JSONArray();\n\t\tfor (final Task task : getTasksIn(list)) {\n\t\t\tfinal JSONObject jsontask = new JSONObject();\n\t\t\tjsontask.put(Task.Columns._ID, task._id);\n\t\t\taddAllContentToJSON(task.getContent(), jsontask);\n\t\t\tjsontask.put(Task.Columns.LEFT, task.left);\n\t\t\tjsontask.put(Task.Columns.RIGHT, task.right);\n\n\t\t\tjsontask.put(KEY_REMOTES, getJSONRemotesFor(task));\n\t\t\tjsontask.put(KEY_REMINDERS, getJSONRemindersFor(task));\n\n\t\t\ttaskarray.put(jsontask);\n\t\t}\n\t\treturn taskarray;\n\t}\n\n\tprivate JSONArray getJSONRemotesFor(final Task task) throws JSONException {\n\t\tfinal JSONArray remotetaskarray = new JSONArray();\n\t\tfor (final RemoteTask remote : getRemotesOf(task)) {\n\t\t\tfinal JSONObject jsonremote = new JSONObject();\n\t\t\tjsonremote.put(RemoteTask.Columns._ID, remote._id);\n\t\t\taddAllContentToJSON(remote.getContent(), jsonremote);\n\n\t\t\tremotetaskarray.put(jsonremote);\n\t\t}\n\t\treturn remotetaskarray;\n\t}\n\n\tprivate JSONArray getJSONRemindersFor(final Task task) throws JSONException {\n\t\tfinal JSONArray reminderarray = new JSONArray();\n\t\tfor (final Notification reminder : getRemindersFor(task)) {\n\t\t\tfinal JSONObject jsonreminder = new JSONObject();\n\t\t\tjsonreminder.put(Notification.Columns._ID, reminder._id);\n\t\t\taddAllContentToJSON(reminder.getContent(), jsonreminder);\n\n\t\t\treminderarray.put(jsonreminder);\n\t\t}\n\t\treturn reminderarray;\n\t}\n\n\t/**\n\t * Backs up the entire database to a JSON file. The location and name of the\n\t * file are hardcoded.\n\t */\n\tpublic void writeBackup() throws JSONException, IOException, SecurityException {\n\t\t// Create JSON object\n\t\tfinal JSONObject backup = getJSONBackup();\n\n\t\tvar uri = BackupPrefs.getSelectedBackupDirUri(this.context);\n\t\t// user didn't choose a folder. This is checked before this function runs\n\t\tif (uri == null) throw new IOException();\n\n\t\tvar newFile = DocumentFileHelper.createBackupJsonFile(this.context);\n\t\tif (newFile == null || !newFile.exists() || !newFile.canWrite()) {\n\t\t\t// it isn't a matter of permissions, the S.A.F. doesn't need permissions\n\t\t\tNnnLogger.error(JSONBackup.class, \"Can't access documentfile\");\n\t\t\tthrow new IOException();\n\t\t}\n\n\t\tString json = backup.toString(2);\n\t\tDocumentFileHelper.write(json, newFile, this.context);\n\t}\n\n\t/**\n\t * Clears the database and restores the backup. Throws exceptions on\n\t * failure.\n\t */\n\tpublic void restoreBackup() throws SecurityException, JSONException, IOException {\n\t\tfinal JSONObject backup = readBackup();\n\t\t// Only if backup exists will we clear the database\n\t\tclearDatabase();\n\n\t\tfinal JSONArray listsarray = backup.getJSONArray(KEY_LISTS);\n\t\tfor (int i = 0; i < listsarray.length(); i++) {\n\t\t\tfinal JSONObject jsonlist = listsarray.getJSONObject(i);\n\t\t\tfinal TaskList tasklist = new TaskList(jsonlist);\n\t\t\tif (tasklist.updated != null)\n\t\t\t\ttasklist.save(context, tasklist.updated);\n\t\t\telse\n\t\t\t\ttasklist.save(context);\n\n\t\t\tif (!jsonlist.isNull(KEY_REMOTES)) {\n\t\t\t\trestoreRemotes(tasklist, jsonlist.getJSONArray(KEY_REMOTES));\n\t\t\t} else {\n\t\t\t\tLog.d(\"JONAS\", \"Remotes was null\");\n\t\t\t}\n\t\t\tif (!jsonlist.isNull(KEY_TASKS)) {\n\t\t\t\trestoreTasks(tasklist, jsonlist.getJSONArray(KEY_TASKS));\n\t\t\t}\n\t\t}\n\n\t\t// Schedule notifications\n\t\tNotificationHelper.schedule(context);\n\t}\n\n\tprivate void clearDatabase() {\n\t\tcontext.getContentResolver().delete(RemoteTask.URI, null, null);\n\t\tcontext.getContentResolver().delete(RemoteTaskList.URI, null, null);\n\n\t\tcontext.getContentResolver().delete(TaskList.URI, null, null);\n\n\t\tcontext.getContentResolver().delete(Task.URI, null, null);\n\t\tcontext.getContentResolver().delete(Notification.URI, null, null);\n\n\t}\n\n\tprivate JSONObject readBackup() throws JSONException, IOException, SecurityException {\n\t\tvar fileDoc = DocumentFileHelper.getSelectedBackupJsonFile(this.context);\n\t\tif (fileDoc == null || !fileDoc.exists() || !fileDoc.canRead()) {\n\t\t\t// it isn't a matter of permissions, the S.A.F. doesn't need permissions\n\t\t\tNnnLogger.error(JSONBackup.class, \"Can't access the documentfile\");\n\t\t\tthrow new IOException(\"Can't access the documentfile\");\n\t\t}\n\t\tInputStream inSt = this.context\n\t\t\t\t.getContentResolver()\n\t\t\t\t.openInputStream(fileDoc.getUri());\n\t\tfinal StringBuilder sb = new StringBuilder();\n\t\tString line;\n\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(inSt));\n\t\twhile ((line = reader.readLine()) != null) {\n\t\t\tsb.append(line);\n\t\t}\n\n\t\treturn new JSONObject(sb.toString());\n\t}\n\n\tprivate void restoreRemotes(final TaskList tasklist,\n\t\t\t\t\t\t\t\tfinal JSONArray jsonArray) throws JSONException {\n\t\tLog.d(\"JONAS\", \"Remote length: \" + jsonArray.length());\n\t\tfor (int i = 0; i < jsonArray.length(); i++) {\n\t\t\tfinal JSONObject json = jsonArray.getJSONObject(i);\n\t\t\tfinal RemoteTaskList remote = new RemoteTaskList(json);\n\t\t\tremote.dbid = tasklist._id;\n\t\t\tremote.save(context);\n\t\t\tLog.d(\"JONAS\", \"RemoteL restored: \" + remote._id);\n\t\t}\n\t}\n\n\tprivate void restoreTasks(final TaskList list, final JSONArray tasksarray)\n\t\t\tthrows JSONException {\n\t\tfor (int i = 0; i < tasksarray.length(); i++) {\n\t\t\tfinal JSONObject jsontask = tasksarray.getJSONObject(i);\n\t\t\tfinal Task task = new Task(jsontask);\n\t\t\ttask.dblist = list._id;\n\t\t\tif (task.updated != null)\n\t\t\t\ttask.save(context, task.updated);\n\t\t\telse\n\t\t\t\ttask.save(context);\n\n\t\t\tif (!jsontask.isNull(KEY_REMOTES)) {\n\t\t\t\trestoreRemotes(task, jsontask.getJSONArray(KEY_REMOTES));\n\t\t\t}\n\t\t\tif (!jsontask.isNull(KEY_REMINDERS)) {\n\t\t\t\trestoreReminders(task, jsontask.getJSONArray(KEY_REMINDERS));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void restoreRemotes(final Task task, final JSONArray jsonArray)\n\t\t\tthrows JSONException {\n\t\tfor (int i = 0; i < jsonArray.length(); i++) {\n\t\t\tfinal JSONObject json = jsonArray.getJSONObject(i);\n\t\t\tfinal RemoteTask remote = new RemoteTask(json);\n\t\t\tremote.dbid = task._id;\n\t\t\tremote.listdbid = task.dblist;\n\t\t\tremote.save(context);\n\t\t\tLog.d(\"JONAS\", \"RemoteT restored: \" + remote._id);\n\t\t}\n\t}\n\n\tprivate void restoreReminders(final Task task, final JSONArray jsonArray)\n\t\t\tthrows JSONException {\n\t\tfor (int i = 0; i < jsonArray.length(); i++) {\n\t\t\tfinal JSONObject json = jsonArray.getJSONObject(i);\n\t\t\tfinal Notification not = new Notification(json);\n\t\t\tnot.taskID = task._id;\n\t\t\tnot.save(context);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/googleapi/GoogleTask.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.googleapi;\n\nimport com.nononsenseapps.helpers.RFC3339Date;\nimport com.nononsenseapps.notepad.database.RemoteTask;\nimport com.nononsenseapps.notepad.database.Task;\n\npublic class GoogleTask extends RemoteTask {\n\n\tpublic static final String ID = \"id\";\n\tpublic static final String TITLE = \"title\";\n\tpublic static final String UPDATED = \"updated\";\n\tpublic static final String NOTES = \"notes\";\n\tpublic static final String STATUS = \"status\";\n\tpublic static final String DUE = \"due\";\n\tpublic static final String DELETED = \"deleted\";\n\tpublic static final String COMPLETED = \"completed\";\n\tpublic static final String NEEDSACTION = \"needsAction\";\n\tpublic static final String PARENT = \"parent\";\n\tpublic static final String POSITION = \"position\";\n\tpublic static final String HIDDEN = \"hidden\";\n\n\t// all of these should be changed to methods like getTitle() { return this.title; }\n\t// and setTitle(String new) { this.title = new; } but as of now google task is\n\t// not even used by the app...\n\tpublic String title = null;\n\tpublic String notes = null;\n\tpublic String status = null;\n\tpublic String dueDate = null;\n\tpublic String parent = null;\n\tpublic String position = null;\n\n\tpublic boolean remotelydeleted = false;\n\n\tpublic final String possort = \"\";\n\n\tpublic GoogleTask(final Task dbTask, final String accountName) {\n\t\tsuper();\n\t\tthis.service = GoogleTaskList.SERVICENAME;\n\t\taccount = accountName;\n\t\tif (dbTask != null)\n\t\t\tfillFrom(dbTask);\n\t}\n\n\n\tpublic void fillFrom(final Task dbTask) {\n\t\ttitle = dbTask.title;\n\t\tnotes = dbTask.note;\n\t\tdueDate = RFC3339Date.asRFC3339ZuluDate(dbTask.due);\n\t\tstatus = dbTask.completed != null ? GoogleTask.COMPLETED\n\t\t\t\t: GoogleTask.NEEDSACTION;\n\t\tremotelydeleted = false;\n\t\tdeleted = null;\n\t\tdbid = dbTask._id;\n\t\tlistdbid = dbTask.dblist;\n\t}\n\n\t/**\n\t * Returns true if the task has the same remote id or same database id.\n\t */\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tboolean equal = false;\n\t\tif (o instanceof GoogleTask task) {\n\t\t\t// It's a list!\n\t\t\tif (dbid != -1 && dbid.equals(task.dbid)) {\n\t\t\t\tequal = true;\n\t\t\t}\n\t\t\tif (remoteId != null && remoteId.equals(task.remoteId)) {\n\t\t\t\tequal = true;\n\t\t\t}\n\t\t}\n\t\treturn equal;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/googleapi/GoogleTaskList.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.googleapi;\n\nimport com.nononsenseapps.notepad.database.RemoteTaskList;\nimport com.nononsenseapps.notepad.database.TaskList;\n\npublic class GoogleTaskList extends RemoteTaskList {\n\n\tpublic static final String SERVICENAME = \"googletasks\";\n\tpublic String title = null;\n\n\tpublic GoogleTaskList(final TaskList dbList, final String accountName) {\n\t\tsuper();\n\t\tthis.title = dbList.title;\n\t\tthis.dbid = dbList._id;\n\t\tthis.account = accountName;\n\t\tthis.service = SERVICENAME;\n\t}\n\n\tpublic GoogleTaskList(final Long dbid, final String remoteId, final Long updated, final String account) {\n\t\tsuper(dbid, remoteId, updated, account);\n\t\tthis.service = SERVICENAME;\n\t}\n\n\t/**\n\t * Returns true if the TaskList has the same remote id or the same database\n\t * id.\n\t */\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tboolean equal = false;\n\t\tif (o instanceof GoogleTaskList list) {\n\t\t\t// It's a list!\n\t\t\tif (dbid != -1 && dbid.equals(list.dbid)) {\n\t\t\t\tequal = true;\n\t\t\t}\n\t\t\tif (remoteId != null && remoteId.equals(list.remoteId)) {\n\t\t\t\tequal = true;\n\t\t\t}\n\t\t}\n\t\treturn equal;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/BackgroundSyncScheduler.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.app.AlarmManager;\nimport android.app.PendingIntent;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.SystemClock;\n\nimport androidx.annotation.NonNull;\n\nimport com.nononsenseapps.helpers.NnnLogger;\n\npublic class BackgroundSyncScheduler extends BroadcastReceiver {\n\n\t// Unique ID for schedule\n\tprivate final static int scheduleCode = 2832;\n\n\tpublic BackgroundSyncScheduler() {}\n\n\t@Override\n\tpublic void onReceive(Context context, @NonNull Intent intent) {\n\t\tNnnLogger.debug(BackgroundSyncScheduler.class,\n\t\t\t\t\"Received intent with action = \" + intent.getAction());\n\n\t\tfinal boolean enabled = OrgSyncService.areAnyEnabled(context);\n\t\tif (enabled && Intent.ACTION_RUN.equals(intent.getAction())) {\n\t\t\t// Run sync\n\t\t\tOrgSyncService.start(context);\n\t\t} else {\n\t\t\tscheduleSync(context);\n\t\t}\n\t}\n\n\t/**\n\t * Schedule a synchronization for later.\n\t */\n\tpublic static void scheduleSync(final Context context) {\n\t\tfinal AlarmManager alarmManager = (AlarmManager) context\n\t\t\t\t.getSystemService(Context.ALARM_SERVICE);\n\t\tfinal Intent action = new Intent(context, BackgroundSyncScheduler.class) // EXPLICIT intent\n\t\t\t\t.setAction(Intent.ACTION_RUN);\n\t\tfinal PendingIntent operation = PendingIntent.getBroadcast(context, scheduleCode, action,\n\t\t\t\tPendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\t\tif (OrgSyncService.areAnyEnabled(context)) {\n\t\t\t// Schedule syncs\n\t\t\t// Repeat at inexact intervals and do NOT wake the device up.\n\t\t\talarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,\n\t\t\t\t\tSystemClock.elapsedRealtime(),\n\t\t\t\t\tAlarmManager.INTERVAL_HALF_HOUR, // gets ignored anyway\n\t\t\t\t\toperation);\n\t\t} else {\n\t\t\t// Remove schedule\n\t\t\talarmManager.cancel(operation);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/DBSyncBase.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport com.nononsenseapps.notepad.database.RemoteTask;\nimport com.nononsenseapps.notepad.database.RemoteTaskList;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport org.cowboyprogrammer.org.OrgFile;\nimport org.cowboyprogrammer.org.OrgNode;\nimport org.cowboyprogrammer.org.OrgTimestamp;\nimport org.cowboyprogrammer.org.parser.RegexParser;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.text.ParseException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\n\n/**\n * This class is suitable for synchronizers to inherit from. It contains the\n * necessary logic to handle the database communication and conversions.\n */\npublic abstract class DBSyncBase implements SynchronizerInterface {\n\n\tprotected final Context context;\n\tprivate final ContentResolver resolver;\n\n\tpublic DBSyncBase(final Context context) {\n\t\tthis.context = context;\n\t\tthis.resolver = context.getContentResolver();\n\t}\n\n\t/**\n\t * Reads the database and the OrgFile. Returns the matching Tasks and Nodes.\n\t * <p/>\n\t * TODO\n\t *  For gods' sake, test me!\n\t *\n\t * @param file The OrgFile containing all the tasks\n\t * @param list The TaskList corresponding to the OrgFile.\n\t * @return A list of all task-related objects necessary for synchronization.\n\t */\n\tprotected List<Pair<OrgNode, Pair<RemoteTask, Task>>> getNodesAndDBEntries(\n\t\t\tOrgFile file, TaskList list) {\n\t\tfinal List<Pair<OrgNode, Pair<RemoteTask, Task>>> result = new ArrayList<>();\n\n\t\tfinal HashMap<Long, Task> tasks = getTasks(list);\n\n\t\tfinal HashMap<Long, RemoteTask> remotes = getValidRemoteTasks(list);\n\n\t\tfinal List<RemoteTask> remotesDeleted = getInvalidRemoteTasks(list);\n\n\t\tfinal HashMap<String, OrgNode> nodes = getNodes(file);\n\n\t\t// Start with tasks\n\t\tfor (long dbid : tasks.keySet()) {\n\t\t\tTask task = tasks.get(dbid);\n\t\t\tRemoteTask remote = remotes.remove(dbid);\n\t\t\tOrgNode node = null;\n\t\t\t// Can be null\n\t\t\tif (remote != null) {\n\t\t\t\tnode = nodes.remove(remote.remoteId.toUpperCase());\n\t\t\t}\n\t\t\tresult.add(new Pair<>(node,\n\t\t\t\t\tnew Pair<>(remote, task)));\n\t\t}\n\t\t// Follow with remaining remotes where task is null\n\t\tfor (RemoteTask remote : remotes.values()) {\n\t\t\tOrgNode node = nodes.remove(remote.remoteId.toUpperCase());\n\t\t\tresult.add(new Pair<>(node, new Pair<>(remote, null)));\n\t\t}\n\t\tfor (RemoteTask remote : remotesDeleted) {\n\t\t\tOrgNode node = nodes.remove(remote.remoteId.toUpperCase());\n\t\t\tresult.add(new Pair<>(node, new Pair<>(remote, null)));\n\t\t}\n\t\t// Last, nodes with no database connections\n\t\tfor (OrgNode node : nodes.values()) {\n\t\t\tresult.add(new Pair<>(node, new Pair<>(/*task=*/ null, /*remote=*/ null)));\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate HashMap<String, OrgNode> getNodes(final OrgFile file) {\n\t\tfinal HashMap<String, OrgNode> map = new HashMap<>();\n\n\t\tfor (OrgNode node : file.getSubNodes()) {\n\t\t\taddNodeToMap(node, map);\n\t\t}\n\n\t\treturn map;\n\t}\n\n\t/**\n\t * By convention, all generated ids are stored in uppercase.\n\t */\n\tprivate void addNodeToMap(final OrgNode node,\n\t\t\t\t\t\t\t  final HashMap<String, OrgNode> map) {\n\t\tString key = OrgConverter.getNodeId(node);\n\t\tLog.d(Synchronizer.TAG, \"Key: \" + key + \", node: \" + node.getComments());\n\t\tif (key == null) {\n\t\t\t// This key won't necessarily be used later.\n\t\t\tkey = OrgConverter.generateId();\n\t\t}\n\t\tmap.put(key.toUpperCase(), node);\n\n\t\tfor (OrgNode subnode : node.getSubNodes()) {\n\t\t\taddNodeToMap(subnode, map);\n\t\t}\n\t}\n\n\tprivate HashMap<Long, RemoteTask> getValidRemoteTasks(final TaskList list) {\n\t\tfinal HashMap<Long, RemoteTask> map = new HashMap<>();\n\t\ttry (Cursor c = resolver.query(\n\t\t\t\tRemoteTask.URI,\n\t\t\t\tRemoteTask.Columns.FIELDS,\n\t\t\t\tRemoteTask.Columns.SERVICE + \" IS ? AND \"\n\t\t\t\t\t\t+ RemoteTask.Columns.ACCOUNT + \" IS ? AND \"\n\t\t\t\t\t\t+ RemoteTask.Columns.LISTDBID + \" IS ? AND \"\n\t\t\t\t\t\t+ RemoteTask.Columns.DBID + \" > 0\",\n\t\t\t\tnew String[] { getServiceName(), getAccountName(),\n\t\t\t\t\t\tLong.toString(list._id) }, null)) {\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tRemoteTask remote = new RemoteTask(c);\n\t\t\t\tmap.put(remote.dbid, remote);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}\n\n\t/**\n\t * These remote tasks are no longer connected to a task.\n\t * This typically happens when a task is\n\t * deleted or moved to another list.\n\t */\n\tprivate List<RemoteTask> getInvalidRemoteTasks(final TaskList list) {\n\t\tfinal ArrayList<RemoteTask> remoteList = new ArrayList<>();\n\t\ttry (Cursor c = resolver.query(\n\t\t\t\tRemoteTask.URI,\n\t\t\t\tRemoteTask.Columns.FIELDS,\n\t\t\t\tRemoteTask.Columns.SERVICE + \" IS ? AND \"\n\t\t\t\t\t\t+ RemoteTask.Columns.ACCOUNT + \" IS ? AND \"\n\t\t\t\t\t\t+ RemoteTask.Columns.LISTDBID + \" IS ? AND \"\n\t\t\t\t\t\t+ RemoteTask.Columns.DBID + \" < 1\",\n\t\t\t\tnew String[] { getServiceName(), getAccountName(),\n\t\t\t\t\t\tLong.toString(list._id) }, null)) {\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tRemoteTask remote = new RemoteTask(c);\n\t\t\t\tremoteList.add(remote);\n\t\t\t}\n\t\t}\n\n\t\treturn remoteList;\n\t}\n\n\tprivate HashMap<Long, Task> getTasks(final TaskList list) {\n\t\tfinal HashMap<Long, Task> map = new HashMap<>();\n\t\ttry (Cursor c = resolver.query(Task.URI, Task.Columns.FIELDS,\n\t\t\t\tTask.Columns.DBLIST + \" IS ?\",\n\t\t\t\tnew String[] { Long.toString(list._id) }, null)) {\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tTask task = new Task(c);\n\t\t\t\tmap.put(task._id, task);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}\n\n\t/**\n\t * Reads the database and the remote source.\n\t *\n\t * @return The matching TaskList and OrgFiles.\n\t */\n\tprotected List<Pair<OrgFile, Pair<RemoteTaskList, TaskList>>> getFilesAndDBEntries()\n\t\t\tthrows IOException, ParseException {\n\t\tfinal List<Pair<OrgFile, Pair<RemoteTaskList, TaskList>>> result = new ArrayList<>();\n\n\t\t// get all lists\n\t\tfinal HashMap<Long, TaskList> lists = getLists();\n\n\t\t// get all db entries\n\t\tfinal HashMap<Long, RemoteTaskList> remotes = getRemoteTaskLists();\n\n\t\t// get all files\n\t\tfinal HashSet<String> filenames = getRemoteFilenames();\n\t\tfor (String filename : filenames) {\n\t\t\tLog.d(Synchronizer.TAG, \"Get Filename: \" + filename);\n\t\t}\n\n\t\t// Construct pairs from lists first. This removes entries as it goes.\n\t\tfor (Long dbid : lists.keySet()) {\n\t\t\tTaskList list = lists.get(dbid);\n\t\t\tRemoteTaskList remote = remotes.remove(dbid);\n\t\t\tOrgFile file = null;\n\t\t\t// Can be null\n\t\t\tif (remote != null && filenames.remove(remote.remoteId)) {\n\t\t\t\tfinal BufferedReader br = getRemoteFile(remote.remoteId);\n\t\t\t\tif (br != null) {\n\t\t\t\t\tfile = OrgFile.createFromBufferedReader(\n\t\t\t\t\t\t\tnew RegexParser(), remote.remoteId, br);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// list title, if available\n\t\t\tString l = list == null ? null : list.title;\n\n\t\t\tString r = null;\n\t\t\tif (remote != null) r = remote.remoteId;\n\t\t\tString f = null;\n\t\t\tif (file != null) f = file.getFilename();\n\t\t\tLog.d(Synchronizer.TAG, \"Pair:\" + l + \", \" + r + \", \" + f);\n\t\t\tresult.add(new Pair<>(file, new Pair<>(remote, list)));\n\t\t}\n\n\t\t// Add remotes that no longer have a list\n\t\tfor (RemoteTaskList remote : remotes.values()) {\n\t\t\tOrgFile file = null;\n\t\t\t// Can be null\n\t\t\tif (remote != null && filenames.remove(remote.remoteId)) {\n\t\t\t\tfinal BufferedReader br = getRemoteFile(remote.remoteId);\n\t\t\t\tif (br != null) {\n\t\t\t\t\tfile = OrgFile.createFromBufferedReader(\n\t\t\t\t\t\t\tnew RegexParser(), remote.remoteId, br);\n\t\t\t\t}\n\t\t\t}\n\t\t\tString r = null;\n\t\t\tif (remote != null)\n\t\t\t\tr = remote.remoteId;\n\t\t\tString f = null;\n\t\t\tif (file != null)\n\t\t\t\tf = file.getFilename();\n\t\t\tLog.d(Synchronizer.TAG, \"Pair:\" + \"(null)\" + \", \" + r + \", \" + f);\n\t\t\tresult.add(new Pair<>(file,\n\t\t\t\t\tnew Pair<>(remote, null)));\n\t\t}\n\n\t\t// Add files that do not exist in database\n\t\tfor (String filename : filenames) {\n\t\t\tOrgFile file = null;\n\t\t\tfinal BufferedReader br = getRemoteFile(filename);\n\t\t\tif (br != null) {\n\t\t\t\tfile = OrgFile.createFromBufferedReader(new RegexParser(), filename, br);\n\t\t\t}\n\t\t\tString f;\n\t\t\t// An obvious precaution. If everything is null, there's nothing to add.\n\t\t\tif (file != null) {\n\t\t\t\tf = file.getFilename();\n\t\t\t\tLog.d(Synchronizer.TAG, \"Pair:\" + \"(null)\" + \", \" + \"(null)\" + \", \" + f);\n\t\t\t\tresult.add(new Pair<>(file, new Pair<>(/*remote=*/null, /*list=*/null)));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * @return a map from list-dbid to RemoteTaskList\n\t */\n\tprivate HashMap<Long, RemoteTaskList> getRemoteTaskLists() {\n\t\tfinal HashMap<Long, RemoteTaskList> map = new HashMap<>();\n\t\ttry (Cursor c = resolver.query(RemoteTaskList.URI,\n\t\t\t\tRemoteTaskList.Columns.FIELDS, RemoteTaskList.Columns.SERVICE\n\t\t\t\t\t\t+ \" IS ? AND \" + RemoteTask.Columns.ACCOUNT + \" IS ?\",\n\t\t\t\tnew String[] { getServiceName(), getAccountName() }, null)) {\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tRemoteTaskList remote = new RemoteTaskList(c);\n\t\t\t\tLog.d(Synchronizer.TAG, \"Get remote: \" + remote.remoteId);\n\t\t\t\tmap.put(remote.dbid, remote);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}\n\n\t/**\n\t * @return a map from list-dbid to TaskList\n\t */\n\tprivate HashMap<Long, TaskList> getLists() {\n\t\tfinal HashMap<Long, TaskList> map = new HashMap<>();\n\t\ttry (Cursor c = resolver.query(TaskList.URI, TaskList.Columns.FIELDS,\n\t\t\t\tnull, null, null)) {\n\t\t\twhile (c.moveToNext()) {\n\t\t\t\tTaskList list = new TaskList(c);\n\t\t\t\tLog.d(Synchronizer.TAG, \"Get list: \" + list.title);\n\t\t\t\tmap.put(list._id, list);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}\n\n\t/**\n\t * Make sure notifications are synchronized from node to database.\n\t */\n\tprotected void replaceNotifications(final Task task, final OrgNode node) {\n\t\t// TODO Auto-generated method stub\n\t\t// Remove existing notifications\n\n\t\t// Add new notifications\n\t\tfor (OrgTimestamp ts : node.getTimestamps()) {\n\t\t\tif (!ts.isInactive()) {\n\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean wasRenamed(final TaskList list, final OrgFile file) {\n\t\treturn !(OrgConverter.getTitleAsFilename(list)).equals(file.getFilename());\n\t}\n\n\t/**\n\t * (re)Names a file to match the DB version's current name.\n\t *\n\t * @param list    Current version in the database\n\t * @param dbEntry Current remote version in the database which will also be\n\t *                renamed.\n\t * @param file    File to rename.\n\t */\n\tprotected void renameFile(final TaskList list,\n\t\t\t\t\t\t\t  final RemoteTaskList dbEntry, final OrgFile file) {\n\t\tif (list.title != null && !list.title.isEmpty()) {\n\t\t\tfile.setFilename(OrgConverter.getTitleAsFilename(list));\n\t\t}\n\t\tdbEntry.remoteId = file.getFilename();\n\t\tdbEntry.save(context);\n\t}\n\n\t/**\n\t * Delete remote versions of tasks to current service.\n\t *\n\t * @param listdbid List they belong to.\n\t */\n\tprivate void deleteRemoteTasksIn(final long listdbid) {\n\t\tcontext.getContentResolver().delete(\n\t\t\t\tRemoteTask.URI,\n\t\t\t\tRemoteTask.Columns.SERVICE + \" IS ? AND \" + RemoteTask.Columns.ACCOUNT +\n\t\t\t\t\t\t\" IS ? AND \" + RemoteTask.Columns.LISTDBID + \" IS ?\",\n\t\t\t\tnew String[] { getServiceName(), getAccountName(), Long.toString(listdbid) });\n\t}\n\n\t/**\n\t * Deletes a list and all tasks and related entries (to current service).\n\t * Call this when remote file has been deleted.\n\t *\n\t * @param list    List to delete. Can be null.\n\t * @param dbEntry RemoteEntry in DB to delete. Can be null.\n\t */\n\tprotected void deleteLocal(final TaskList list, final RemoteTaskList dbEntry) {\n\t\tlong listdbid = -1;\n\t\tif (list != null) {\n\t\t\tlist.delete(context);\n\t\t\tlistdbid = list._id;\n\t\t}\n\t\tif (dbEntry != null) {\n\t\t\tdbEntry.delete(context);\n\t\t\tlistdbid = dbEntry.dbid;\n\t\t}\n\t\t// Tasks are deleted automatically, but not the\n\t\t// remote-versions\n\t\tdeleteRemoteTasksIn(listdbid);\n\t}\n\n\t/**\n\t * Deletes a task and dbEntry from database.\n\t *\n\t * @param task    Task to delete, can be null.\n\t * @param dbEntry dbEntry to delete, can be null.\n\t */\n\tprotected void deleteLocal(final Task task, final RemoteTask dbEntry) {\n\t\tif (task != null) {\n\t\t\ttask.delete(context);\n\t\t}\n\t\tif (dbEntry != null) {\n\t\t\tdbEntry.delete(context);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/Monitor.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.nononsenseapps.notepad.sync.orgsync;\n\n/**\n * An interface which defines a \"Monitor\". A monitor is an object which\n * monitors a specific sync source for changes, such as a FileMonitor.\n */\npublic interface Monitor {\n\n\t/**\n\t * Start monitoring. Call handler on changes.\n\t */\n\tvoid startMonitor(final OrgSyncService.SyncHandler handler);\n\n\t/**\n\t * Pausing, it might be restarted later.\n\t */\n\tvoid pauseMonitor();\n\n\t/**\n\t * Service is destroying itself. Remove any references.\n\t */\n\tvoid terminate();\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgConverter.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.annotation.SuppressLint;\n\nimport androidx.annotation.Nullable;\n\nimport com.nononsenseapps.notepad.database.Notification;\nimport com.nononsenseapps.notepad.database.RemoteTask;\nimport com.nononsenseapps.notepad.database.RemoteTaskList;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport org.cowboyprogrammer.org.OrgFile;\nimport org.cowboyprogrammer.org.OrgNode;\nimport org.cowboyprogrammer.org.OrgTimestamp;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * This class handles conversion from the internal database format to org-mode\n * fileformat\n */\npublic class OrgConverter {\n\n\tprivate static final String LISTSTYLECOMMENT = \"# NONSENSESTYLE: \";\n\tprivate static final String LISTSORTCOMMENT = \"# NONSENSESORTING: \";\n\tprivate static final String TASKNODEID = \"# NONSENSEID: \";\n\tprivate static final Pattern PatternStyle = Pattern.compile(\n\t\t\t\"#\\\\s*NONSENSESTYLE:\\\\s*(.+)\\\\s*?\", Pattern.CASE_INSENSITIVE);\n\tprivate static final Pattern PatternSorting = Pattern\n\t\t\t.compile(\"#\\\\s*NONSENSESORTING:\\\\s*(.+)\\\\s*?\",\n\t\t\t\t\tPattern.CASE_INSENSITIVE);\n\t// Ending white space used when removed\n\tprivate static final String NonsenseIdPattern = \"#\\\\s*NONSENSEID:\\\\s*(\\\\w+)\\\\s*\";\n\tprivate static final Pattern PatternId = Pattern.compile(NonsenseIdPattern,\n\t\t\tPattern.CASE_INSENSITIVE);\n\tprivate static final String TAG = \"OrgConverter\";\n\tprivate static Random rand;\n\n\t/**\n\t * Generates an id for RemoteTask(List) objects.\n\t */\n\tpublic static String generateId() {\n\t\tfinal int len = 8;\n\t\tif (rand == null) {\n\t\t\trand = new Random();\n\t\t}\n\t\tString hex = Integer.toHexString(rand.nextInt());\n\t\t// Pad with zeros if too short\n\t\twhile (hex.length() < len) {\n\t\t\thex = \"0\".concat(hex);\n\t\t}\n\t\treturn hex.substring(0, len);\n\t}\n\n\t/**\n\t * Fill in all the properties of the file that should go in the TaskList\n\t * object.\n\t */\n\tpublic static void toListFromFile(final TaskList list, final OrgFile file) {\n\t\t// Minus .org extension\n\t\tlist.title = file.getFilename().substring(0,\n\t\t\t\tfile.getFilename().length() - 4);\n\t\tlist.sorting = getListSortingFromMeta(file);\n\t\tlist.listtype = getListTypeFromMeta(file);\n\t}\n\n\t/**\n\t * Reads comment section of file. Returns null if not found.\n\t */\n\tpublic static String getListTypeFromMeta(final OrgFile file) {\n\t\tfinal Matcher m = PatternStyle.matcher(file.getComments());\n\t\tif (m.find()) {\n\t\t\treturn m.group(1);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Reads comment section of file. Returns null if not found.\n\t */\n\tpublic static String getListSortingFromMeta(final OrgFile file) {\n\t\tfinal Matcher m = PatternSorting.matcher(file.getComments());\n\t\tif (m.find()) {\n\t\t\treturn m.group(1);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Fill in all the properties of the file that should go in the\n\t * RemoteTaskList object.\n\t */\n\tpublic static void toRemoteFromFile(final RemoteTaskList entry,\n\t\t\t\t\t\t\t\t\t\tfinal OrgFile file) {\n\t\tentry.remoteId = file.getFilename();\n\t\tRemoteTaskListFile.setSorting(entry, getListSortingFromMeta(file));\n\t\tRemoteTaskListFile.setListType(entry, getListTypeFromMeta(file));\n\t\tentry.updated = Calendar.getInstance().getTimeInMillis();\n\t}\n\n\t/**\n\t * Fill in all the properties of the node that should go in the Task object.\n\t */\n\tpublic static void toTaskFromNode(final Task task, final OrgNode node) {\n\t\ttask.title = node.getTitle();\n\t\ttask.due = getDeadline(node);\n\t\ttask.completed = getCompleted(node);\n\t\ttask.note = node.getBody();\n\n\t\t/*\n\t\t * It's not possible to differentiate if the user added a trailing\n\t\t * newline or the sync logic did. I will assume that the sync logic did.\n\t\t */\n\t\tif (task.note != null && task.note.endsWith(\"\\n\")) {\n\t\t\ttask.note = task.note.substring(0, task.note.length() - 1);\n\t\t}\n\t}\n\n\t/**\n\t * Fill in all the properties of the nodes from the task object.\n\t */\n\tpublic static void toNodeFromTask(final Task task, final OrgNode node) {\n\t\tnode.setLevel(1);\n\t\tnode.setTitle(task.title);\n\t\tnode.setBody(task.note);\n\t\tsetTodo(node, task.completed);\n\t\tremoveTimestamps(node);\n\t\tsetDeadline(node, task.due);\n\t}\n\n\tprivate static void setNotifications(final OrgNode node, final List<Notification> reminders) {\n\t\tif (reminders == null)\n\t\t\treturn;\n\n\t\tfor (Notification reminder : reminders) {\n\t\t\tif (reminder.radius == null && reminder.time != null) {\n\t\t\t\tOrgTimestamp ts = new OrgTimestamp(reminder.time, true);\n\t\t\t\tts.setInactive(false);\n\t\t\t\tnode.getTimestamps().add(ts);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns NOW as completed if node is DONE. Else null.\n\t */\n\tprivate static Long getCompleted(final OrgNode node) {\n\t\tif (\"DONE\".equals(node.getTodo())) {\n\t\t\treturn new Date().getTime();\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static void setTodo(final OrgNode node, final Long completed) {\n\t\tif (completed == null) {\n\t\t\tnode.setTodo(\"TODO\");\n\t\t} else {\n\t\t\tnode.setTodo(\"DONE\");\n\t\t}\n\t}\n\n\t/**\n\t * Return the (first) deadline of the object, or null\n\t */\n\tpublic static Long getDeadline(final OrgNode node) {\n\t\tfor (OrgTimestamp ts : node.getTimestamps()) {\n\t\t\tif (OrgTimestamp.Type.DEADLINE == ts.getType()) {\n\t\t\t\treturn ts.getDate().toDate().getTime();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static void removeTimestamps(final OrgNode node) {\n\t\tnode.getTimestamps().clear();\n\t}\n\n\tpublic static void setDeadline(final OrgNode node, final Long due) {\n\t\tnode.getTimestamps().clear();\n\t\t// Add deadline if not null\n\t\tif (due != null) {\n\t\t\tOrgTimestamp ts = new OrgTimestamp(due, false);\n\t\t\tts.setType(OrgTimestamp.Type.DEADLINE);\n\t\t\tnode.getTimestamps().add(ts);\n\t\t}\n\t}\n\n\t/**\n\t * Remove a possible comment containing a nonsenseid\n\t */\n\tprivate static String getOrgBodySansId(final OrgNode node) {\n\t\treturn node.getOrgBody().replaceFirst(NonsenseIdPattern, \"\");\n\t}\n\n\t/**\n\t * Fill in all the properties of the node that should go in the RemoteTask\n\t * object. If no ID is set, then an ID is added to both the entry and the\n\t * node. This method returns true if an id was added to the node, and it\n\t * should be updated in file.\n\t */\n\tpublic static boolean toRemoteFromNode(final RemoteTask dbEntry,\n\t\t\t\t\t\t\t\t\t\t   final OrgNode node) {\n\t\tboolean addedToNode = false;\n\t\tif (dbEntry.remoteId == null) {\n\t\t\tString id = getNodeId(node);\n\t\t\tif (id == null) {\n\t\t\t\tid = generateId();\n\t\t\t\taddIdToNode(id, node);\n\t\t\t\taddedToNode = true;\n\t\t\t}\n\t\t\tdbEntry.remoteId = id;\n\t\t}\n\t\tdbEntry.updated = Calendar.getInstance().getTimeInMillis();\n\n\t\tRemoteTaskNode.setTitle(dbEntry, node.getTitle());\n\t\tRemoteTaskNode.setBody(dbEntry, node.getBody());\n\t\tfinal Long t = getDeadline(node);\n\t\tString s = null;\n\t\tif (t != null)\n\t\t\ts = Long.toString(t);\n\t\tRemoteTaskNode.setDueTime(dbEntry, s);\n\t\tRemoteTaskNode.setTodo(dbEntry, node.getTodo());\n\n\t\treturn addedToNode;\n\t}\n\n\t/**\n\t * Add an id to the meta-section of a node.\n\t */\n\t@SuppressLint(\"DefaultLocale\")\n\tprivate static void addIdToNode(final String id, final OrgNode node) {\n\t\tnode.setComments(TASKNODEID + id.toUpperCase() + \"\\n\");\n\t}\n\n\t/**\n\t * Set the meta section to be only the id. This does not overwrite rest of\n\t * comments since they are stored in the task and sent to the body.\n\t */\n\t@SuppressLint(\"DefaultLocale\")\n\tpublic static void toNodeFromRemote(final OrgNode node,\n\t\t\t\t\t\t\t\t\t\tfinal RemoteTask dbEntry) {\n\t\tnode.setComments(TASKNODEID + dbEntry.remoteId.toUpperCase() + \"\\n\");\n\t}\n\n\t/**\n\t * Returns the id from the meta-section, if present. Null otherwise.\n\t */\n\t@SuppressLint(\"DefaultLocale\")\n\t@Nullable\n\tpublic static String getNodeId(final OrgNode node) {\n\t\tfinal Matcher m = PatternId.matcher(node.getComments());\n\t\tif (m.find()) {\n\t\t\treturn m.group(1).toUpperCase();\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Fills in the information in the file from the list\n\t */\n\tpublic static void toFileFromList(final TaskList list, final OrgFile file) {\n\t\tsetSortingOnFile(list, file);\n\t\tsetListTypeOnFile(list, file);\n\t}\n\n\tpublic static String getTitleAsFilename(TaskList list) {\n\t\treturn list.title + \".org\";\n\t}\n\n\tpublic static void setListTypeOnFile(TaskList list, OrgFile file) {\n\t\tfinal StringBuilder comments = new StringBuilder();\n\t\tif (list.listtype != null) {\n\t\t\tcomments.append(LISTSTYLECOMMENT)\n\t\t\t\t\t.append(list.listtype)\n\t\t\t\t\t.append(\"\\n\");\n\t\t}\n\t\tcomments.append(PatternStyle.matcher(file.getComments()).replaceAll\n\t\t\t\t(\"\").trim());\n\t\tfile.setComments(comments.toString());\n\t}\n\n\tpublic static void setSortingOnFile(final TaskList list, final OrgFile file) {\n\t\tfinal StringBuilder comments = new StringBuilder();\n\t\tif (list.sorting != null) {\n\t\t\tcomments.append(LISTSORTCOMMENT).append(list.sorting).append(\"\\n\");\n\t\t}\n\t\tcomments.append(PatternSorting.matcher(file.getComments()).replaceAll\n\t\t\t\t(\"\").trim());\n\t\tfile.setComments(comments.toString());\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgProvider.java",
    "content": "package com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.nononsenseapps.helpers.FileHelper;\nimport com.nononsenseapps.helpers.PreferencesHelper;\n\nimport org.cowboyprogrammer.org.OrgFile;\nimport org.cowboyprogrammer.org.OrgNode;\nimport org.cowboyprogrammer.org.parser.OrgParser;\nimport org.cowboyprogrammer.org.parser.RegexParser;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.text.ParseException;\n\npublic class OrgProvider extends ContentProvider {\n\n\tpublic static final String KEY_TITLE = \"title\";\n\tpublic static final String AUTHORITY = \"com.nononsenseapps.notepad.orgprovider\";\n\tpublic static final String SCHEME = \"content://\";\n\tpublic static final Uri BASE_URI = Uri.parse(SCHEME + AUTHORITY + \"/file\");\n\tprivate static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);\n\n\tprivate static final int CODE_FILE = 101;\n\tprivate static final int CODE_FILE_ID = 102;\n\n\tstatic {\n\t\turiMatcher.addURI(AUTHORITY, \"file\", CODE_FILE);\n\t\turiMatcher.addURI(AUTHORITY, \"file/*\", CODE_FILE_ID);\n\t}\n\n\tprivate static final OrgParser orgParser = new RegexParser();\n\n\tpublic OrgProvider() {}\n\n\tprivate int deleteItem(Uri uri) {\n\t\tfinal String title = uri.getLastPathSegment();\n\t\tfinal String filename = title + \".org\";\n\t\tfinal File file = new File(getDir(), filename);\n\n\t\tfinal String itemid = getFragment(uri);\n\n\t\ttry {\n\t\t\tOrgFile orgFile = OrgFile.createFromFile(orgParser, file);\n\n\t\t\tOrgNode toDelete = null;\n\t\t\tfor (OrgNode node : orgFile.getSubNodes()) {\n\t\t\t\tString nodeId = OrgConverter.getNodeId(node);\n\n\t\t\t\tif (nodeId != null && nodeId.equalsIgnoreCase(itemid)) {\n\t\t\t\t\ttoDelete = node;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (toDelete != null) {\n\t\t\t\torgFile.getSubNodes().remove(toDelete);\n\t\t\t\twriteToFile(file, orgFile);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t} catch (IOException | ParseException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\tprivate static void writeToFile(File file, OrgFile orgFile) throws IOException {\n\t\tfinal BufferedWriter bw = new BufferedWriter(new FileWriter(file));\n\t\tbw.write(orgFile.treeToString());\n\t\tbw.close();\n\t}\n\n\t/**\n\t * @return 1 = the number of items affected\n\t */\n\tprivate int deleteFile(Uri uri) {\n\t\tfinal String title = uri.getLastPathSegment();\n\t\tfinal String filename = title + \".org\";\n\t\tfinal File file = new File(getDir(), filename);\n\n\t\tif (file.isFile() && file.exists()) {\n\t\t\tif (file.delete()) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\tthrow new RuntimeException(\"Failed to delete \" + file);\n\t}\n\n\t@Nullable\n\tprivate String getFragment(@NonNull Uri uri) {\n\t\tString fragment = uri.getFragment();\n\t\tif (fragment != null && fragment.isEmpty()) {\n\t\t\tthrow new UnsupportedOperationException(\"Empty URI fragments are now allowed\");\n\t\t}\n\t\treturn fragment;\n\t}\n\n\tprivate Uri insertItem(Uri uri, ContentValues values) {\n\t\t// todo\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\tprivate Uri insertFile(Uri uri, ContentValues values) {\n\t\tfinal String title = values.getAsString(KEY_TITLE);\n\t\tif (title.contains(\"/\")) {\n\t\t\tthrow new IllegalArgumentException(\"Filenames cannot contain slashes\");\n\t\t}\n\t\tif (title.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(\"Filenames cannot be empty\");\n\t\t}\n\t\tfinal String filename = title + \".org\";\n\t\tfinal File file = new File(getDir(), filename);\n\n\t\ttry {\n\t\t\tif (!file.createNewFile()) {\n\t\t\t\tthrow new IllegalArgumentException(\"Failed to create file: \" + filename);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t\treturn Uri.withAppendedPath(BASE_URI, title);\n\t}\n\n\tprivate String getDir() {\n\t\treturn FileHelper.getUserSelectedOrgDir(getContext());\n\t}\n\n\t@Override\n\tpublic boolean onCreate() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic Cursor query(@NonNull Uri uri, String[] projection, String selection,\n\t\t\t\t\t\tString[] selectionArgs, String sortOrder) {\n\t\tif (!PreferencesHelper.isSdSyncEnabled(getContext())) {\n\t\t\treturn null;\n\t\t}\n\t\t// TODO: Implement this to handle query requests from clients.\n\n\t\t// Nullable\n\t\tString fragment = getFragment(uri);\n\n\t\tswitch (uriMatcher.match(uri)) {\n\t\t\tcase CODE_FILE -> {\n\t\t\t\treturn queryFiles(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\t}\n\t\t\tcase CODE_FILE_ID -> {\n\t\t\t\treturn fragment == null ?\n\t\t\t\t\t\tqueryFile(uri, projection, selection, selectionArgs, sortOrder) :\n\t\t\t\t\t\tqueryItem(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\t}\n\t\t}\n\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic String getType(@NonNull Uri uri) {\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic Uri insert(@NonNull Uri uri, ContentValues values) {\n\t\tif (!PreferencesHelper.isSdSyncEnabled(getContext())) {\n\t\t\treturn null;\n\t\t}\n\t\t// TODO: Implement this to handle requests to insert a new row.\n\n\t\tswitch (uriMatcher.match(uri)) {\n\t\t\tcase CODE_FILE -> {\n\t\t\t\treturn insertFile(uri, values);\n\t\t\t}\n\t\t\tcase CODE_FILE_ID -> {\n\t\t\t\treturn insertItem(uri, values);\n\t\t\t}\n\t\t}\n\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {\n\t\tif (!PreferencesHelper.isSdSyncEnabled(getContext())) {\n\t\t\treturn -1;\n\t\t}\n\t\t// Implement this to handle requests to delete one or more rows.\n\t\tString fragment = getFragment(uri);\n\n\t\tif (uriMatcher.match(uri) == CODE_FILE_ID) {\n\t\t\treturn fragment == null ? deleteFile(uri) : deleteItem(uri);\n\t\t}\n\n\t\tthrow new UnsupportedOperationException(\"Delete not supported for \" + uri);\n\t}\n\n\t@Override\n\tpublic int update(@NonNull Uri uri, ContentValues values, String selection,\n\t\t\t\t\t  String[] selectionArgs) {\n\t\tif (!PreferencesHelper.isSdSyncEnabled(getContext())) {\n\t\t\treturn -1;\n\t\t}\n\t\t// TODO: Implement this to handle requests to update one or more rows.\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\tprivate Cursor queryItem(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t String[] selectionArgs, String sortOrder) {\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\tprivate Cursor queryFile(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t String[] selectionArgs, String sortOrder) {\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n\n\tprivate Cursor queryFiles(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t  String[] selectionArgs, String sortOrder) {\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgSyncService.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.app.Service;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.ContentObserver;\nimport android.net.Uri;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.IBinder;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.Process;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.PreferenceManager;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.BuildConfig;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.prefs.SyncPrefs;\nimport com.nononsenseapps.notepad.sync.SyncAdapter;\n\nimport java.io.IOException;\nimport java.text.ParseException;\nimport java.util.ArrayList;\nimport java.util.Calendar;\n\npublic class OrgSyncService extends Service {\n\n\tpublic static final String ACTION_START = BuildConfig.APPLICATION_ID + \".sync.START\";\n\tpublic static final String ACTION_PAUSE = BuildConfig.APPLICATION_ID + \".sync.PAUSE\";\n\n\t// Msg arguments\n\tpublic static final int TWO_WAY_SYNC = 1;\n\tpublic static final int SYNC_QUEUE = 2;\n\tpublic static final int SYNC_RUN = 3;\n\n\tprivate static final int DELAY_MSECS = 30000;\n\n\tprivate SyncHandler serviceHandler;\n\n\tprivate final ArrayList<Monitor> monitors;\n\tprivate final ArrayList<SynchronizerInterface> synchronizers;\n\n\tpublic static void start(Context context) {\n\t\tif (!PreferencesHelper.isSincEnabledAtAll(context)) {\n\t\t\t// not starting: sync is disabled in the prefs\n\t\t\treturn;\n\t\t}\n\n\t\tcontext.startService(new Intent(context, OrgSyncService.class)\n\t\t\t\t.setAction(ACTION_START));\n\t}\n\n\t// TODO this service crashes in API 23 - default image on github\n\n\tpublic static void pause(Context context) {\n\t\tcontext.startService(new Intent(context, OrgSyncService.class)\n\t\t\t\t.setAction(ACTION_PAUSE));\n\t}\n\n\tpublic static void stop(Context context) {\n\t\tcontext.stopService(new Intent(context, OrgSyncService.class));\n\t}\n\n\tpublic static boolean areAnyEnabled(Context context) {\n\t\tif (!PreferencesHelper.isSincEnabledAtAll(context)) return false;\n\t\tif (!PreferencesHelper.isSdSyncEnabled(context)) return false;\n\n\t\treturn true;\n\t}\n\n\tpublic OrgSyncService() {\n\t\tmonitors = new ArrayList<>();\n\t\tsynchronizers = new ArrayList<>();\n\t}\n\n\t/**\n\t * Will only return Synchronizers which have been configured.\n\t *\n\t * @return configured Synchronizers\n\t */\n\tpublic ArrayList<SynchronizerInterface> getSynchronizers() {\n\t\tArrayList<SynchronizerInterface> syncers = new ArrayList<>();\n\n\t\t// Try SD\n\t\tSynchronizerInterface sd = new SDSynchronizer(this);\n\t\tif (sd.isConfigured()) {\n\t\t\tsyncers.add(sd);\n\t\t}\n\n\t\t// TODO if we add another synchronization service, add code here\n\n\t\treturn syncers;\n\t}\n\n\t@Override\n\tpublic void onCreate() {\n\t\t// Start up the thread running the service. Note that we create a\n\t\t// separate thread because the service normally runs in the process's\n\t\t// main thread, which we don't want to block. We also make it\n\t\t// background priority so CPU-intensive work will not disrupt our UI.\n\t\tHandlerThread thread = new HandlerThread(\"ServiceStartArguments\",\n\t\t\t\tProcess.THREAD_PRIORITY_BACKGROUND);\n\t\tthread.start();\n\n\t\t// Get the HandlerThread's Looper and use it for our Handler\n\t\tLooper serviceLooper = thread.getLooper();\n\t\tserviceHandler = new SyncHandler(serviceLooper);\n\t}\n\n\t@Override\n\tpublic int onStartCommand(Intent intent, int flags, int startId) {\n\t\tif (intent != null && intent.getAction() != null &&\n\t\t\t\tACTION_PAUSE.equals(intent.getAction())) {\n\t\t\tpause();\n\t\t} else {\n\t\t\tfinal Message msg = serviceHandler.obtainMessage();\n\t\t\tmsg.arg1 = TWO_WAY_SYNC;\n\t\t\tserviceHandler.sendMessage(msg);\n\t\t}\n\n\t\t// If we get killed, after returning from here, restart\n\t\treturn START_STICKY;\n\t}\n\n\tprivate void pause() {\n\t\t// Pause monitors\n\t\tfor (Monitor monitor : monitors) {\n\t\t\tmonitor.pauseMonitor();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\t// Unregister observers\n\t\tfor (Monitor monitor : monitors) {\n\t\t\tmonitor.terminate();\n\t\t}\n\t}\n\n\t@Override\n\tpublic IBinder onBind(Intent intent) {\n\t\treturn null;\n\t}\n\n\t// Handler that receives messages from the thread\n\tpublic final class SyncHandler extends Handler {\n\n\t\tprivate int changeId = 0;\n\t\tprivate int lastChangeId;\n\n\t\tpublic SyncHandler(Looper looper) {\n\t\t\tsuper(looper);\n\t\t}\n\n\t\tpublic void onMonitorChange() {\n\t\t\tNnnLogger.debug(OrgSyncService.class, \"OnMonitorChange\");\n\t\t\t// Increment the changeId\n\t\t\tchangeId++;\n\n\t\t\t// First queue the operation\n\t\t\tfinal Message q = obtainMessage();\n\t\t\tq.arg1 = SYNC_QUEUE;\n\t\t\tq.arg2 = changeId;\n\t\t\tsendMessage(q);\n\t\t\t// Next, schedule a run in a short delay.\n\t\t\t// Only the run number matching a queue number will run (last one)\n\t\t\tfinal Message r = obtainMessage();\n\t\t\tr.arg1 = SYNC_RUN;\n\t\t\tr.arg2 = changeId;\n\t\t\tsendMessageDelayed(r, DELAY_MSECS);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleMessage(@NonNull Message msg) {\n\n\t\t\tif (synchronizers.isEmpty()) {\n\t\t\t\tsynchronizers.addAll(getSynchronizers());\n\t\t\t}\n\n\t\t\t// Get monitors if empty\n\t\t\tif (monitors.isEmpty()) {\n\t\t\t\t// First db watcher\n\t\t\t\tmonitors.add(new DBWatcher(this));\n\t\t\t\t// Then remote sources\n\t\t\t\tfor (final SynchronizerInterface syncer : synchronizers) {\n\t\t\t\t\tfinal Monitor monitor = syncer.getMonitor();\n\t\t\t\t\tif (monitor != null) {\n\t\t\t\t\t\tmonitors.add(monitor);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\n\t\t\t\t/*\n\t\t\t\t * Queues are used to delay operations until subsequent updates\n\t\t\t\t * are complete.\n\t\t\t\t */\n\t\t\t\tswitch (msg.arg1) {\n\t\t\t\t\tcase SYNC_QUEUE:\n\t\t\t\t\t\tNnnLogger.debug(OrgSyncService.class, \"Sync-Queue: \" + msg.arg2);\n\t\t\t\t\t\tlastChangeId = msg.arg2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SYNC_RUN:\n\t\t\t\t\t\tNnnLogger.debug(OrgSyncService.class, \"Sync-Run: \" + msg.arg2);\n\t\t\t\t\t\tif (msg.arg2 != lastChangeId) {\n\t\t\t\t\t\t\t// Wait...\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Falling through\n\t\t\t\t\tcase TWO_WAY_SYNC:\n\t\t\t\t\t\tNnnLogger.debug(OrgSyncService.class, \"Sync-Two-Way: \" + msg.arg2);\n\t\t\t\t\t\t// Pause monitors\n\t\t\t\t\t\tfor (final Monitor monitor : monitors) {\n\t\t\t\t\t\t\tmonitor.pauseMonitor();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Sync each\n\t\t\t\t\t\tfor (final SynchronizerInterface syncer : synchronizers) {\n\t\t\t\t\t\t\tsendBroadcast(new Intent(SyncAdapter.SYNC_STARTED));\n\t\t\t\t\t\t\tsyncer.fullSync();\n\t\t\t\t\t\t\tsyncer.postSynchronize();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsendBroadcast(new Intent(SyncAdapter.SYNC_FINISHED));\n\t\t\t\t\t\t// Restart monitors\n\t\t\t\t\t\tfor (final Monitor monitor : monitors) {\n\t\t\t\t\t\t\tmonitor.startMonitor(this);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Save last sync time\n\t\t\t\t\t\tPreferenceManager\n\t\t\t\t\t\t\t\t.getDefaultSharedPreferences(OrgSyncService.this)\n\t\t\t\t\t\t\t\t.edit()\n\t\t\t\t\t\t\t\t.putLong(SyncPrefs.KEY_LAST_SYNC,\n\t\t\t\t\t\t\t\t\t\tCalendar.getInstance().getTimeInMillis())\n\t\t\t\t\t\t\t\t.commit();\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t} catch (IOException e) {\n\t\t\t\tNnnLogger.exception(e);\n\t\t\t} catch (ParseException ignored) {}\n\t\t}\n\t}\n\n\tprivate final class DBWatcher extends ContentObserver implements Monitor {\n\n\t\tprivate final SyncHandler handler;\n\n\t\t// Giving it the service handler, onChange will run on that thread\n\t\tpublic DBWatcher(SyncHandler handler) {\n\t\t\tsuper(handler);\n\t\t\tthis.handler = handler;\n\t\t}\n\n\t\tpublic boolean deliverSelfNotifications() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onChange(boolean selfChange) {\n\t\t\tonChange(selfChange, null);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onChange(boolean selfChange, Uri uri) {\n\t\t\thandler.onMonitorChange();\n\t\t}\n\n\t\t@Override\n\t\tpublic void startMonitor(final SyncHandler handler) {\n\t\t\t// Monitor both lists and tasks\n\t\t\tgetContentResolver()\n\t\t\t\t\t.registerContentObserver(TaskList.URI, true, this);\n\t\t\tgetContentResolver()\n\t\t\t\t\t.registerContentObserver(Task.URI, true, this);\n\t\t}\n\n\t\t@Override\n\t\tpublic void pauseMonitor() {\n\t\t\tgetContentResolver().unregisterContentObserver(this);\n\t\t}\n\n\t\t@Override\n\t\tpublic void terminate() {\n\t\t\tpauseMonitor();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/RemoteTaskListFile.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport com.nononsenseapps.notepad.database.RemoteTaskList;\n\npublic class RemoteTaskListFile {\n\n\tpublic static String getSorting(final RemoteTaskList remote) {\n\t\treturn remote.field2;\n\t}\n\n\tpublic static String getListType(final RemoteTaskList remote) {\n\t\treturn remote.field3;\n\t}\n\n\tpublic static void setSorting(final RemoteTaskList remote, final String s) {\n\t\tremote.field2 = s;\n\t}\n\n\tpublic static void setListType(final RemoteTaskList remote, final String s) {\n\t\tremote.field3 = s;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/RemoteTaskNode.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport com.nononsenseapps.notepad.database.RemoteTask;\n\npublic class RemoteTaskNode {\n\n\tpublic static String getTitle(final RemoteTask remote) {\n\t\treturn remote.field2;\n\t}\n\n\tpublic static String getBody(final RemoteTask remote) {\n\t\treturn remote.field3;\n\t}\n\n\tpublic static String getDueTime(final RemoteTask remote) {\n\t\treturn remote.field4;\n\t}\n\n\tpublic static String getTodo(final RemoteTask remote) {\n\t\treturn remote.field5;\n\t}\n\n\tpublic static void setTitle(final RemoteTask remote, final String title) {\n\t\tremote.field2 = title;\n\t}\n\n\tpublic static void setBody(final RemoteTask remote, final String body) {\n\t\tremote.field3 = body;\n\t}\n\n\tpublic static void setDueTime(final RemoteTask remote, final String s) {\n\t\tremote.field4 = s;\n\t}\n\n\tpublic static void setTodo(final RemoteTask remote, final String s) {\n\t\tremote.field5 = s;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/SDSynchronizer.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.os.FileObserver;\nimport android.widget.Toast;\n\nimport com.nononsenseapps.helpers.FileHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.PreferencesHelper;\nimport com.nononsenseapps.notepad.R;\n\nimport org.cowboyprogrammer.org.OrgFile;\nimport org.cowboyprogrammer.org.parser.OrgParser;\nimport org.cowboyprogrammer.org.parser.RegexParser;\n\nimport java.io.BufferedReader;\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.util.HashSet;\n\n/**\n * A synchronizer that that uses an external directory on the SD-card as\n * destination.\n */\npublic class SDSynchronizer extends Synchronizer implements SynchronizerInterface {\n\n\tprivate final static String SERVICENAME = \"SDORG\";\n\n\t/**\n\t * if SD sync is enabled\n\t */\n\tprotected final boolean configured;\n\n\t/**\n\t * Filesystem path of the folder where files are kept. User changeable in preferences.\n\t */\n\tprivate final String ORG_DIR;\n\n\tpublic SDSynchronizer(Context context) {\n\t\tsuper(context);\n\n\t\t// use the user-chosen folder to save ORG files\n\t\tORG_DIR = FileHelper.getUserSelectedOrgDir(context);\n\n\t\tboolean permitted = ORG_DIR != null;\n\t\tif (permitted) {\n\t\t\t// we CAN save files in the external storage\n\t\t\tconfigured = PreferencesHelper.isSdSyncEnabled(context);\n\t\t} else {\n\t\t\tconfigured = false;\n\t\t\tPreferencesHelper.disableSdCardSync(context);\n\t\t}\n\t}\n\n\t/**\n\t * @return A unique name for this service. Should be descriptive, like\n\t * SDOrg or SSHOrg.\n\t */\n\t@Override\n\tpublic String getAccountName() {\n\t\treturn SERVICENAME;\n\t}\n\n\t/**\n\t * @return The username of the configured service. Likely an e-mail.\n\t */\n\t@Override\n\tpublic String getServiceName() {\n\t\treturn SERVICENAME;\n\t}\n\n\t/**\n\t * Returns true if the synchronizer has been configured. This is called\n\t * before synchronization. It will be true if the user has selected an\n\t * account, folder etc...\n\t */\n\t@Override\n\tpublic boolean isConfigured() {\n\t\t// TODO handle errors\n\t\tif (this.configured) {\n\t\t\tFile d = new File(ORG_DIR);\n\t\t\tif (!d.isDirectory()) d.mkdir();\n\t\t\treturn d.isDirectory();\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns an OrgFile object with a filename set that is guaranteed to\n\t * not already exist. Use this method to avoid having multiple objects\n\t * pointing to the same file. Also prevents names with slashes.\n\t *\n\t * @param orgdesiredName The name you'd want. If it exists,\n\t *                       it will be used as the base in desiredName1,\n\t *                       desiredName2, etc. Limited to 99.\n\t * @return an OrgFile guaranteed not to exist.\n\t */\n\t@Override\n\tpublic OrgFile getNewFile(final String orgdesiredName) throws\n\t\t\tIOException, IllegalArgumentException {\n\t\tOrgParser orgParser = new RegexParser();\n\t\t// Replace slashes with underscores\n\t\tString desiredName = orgdesiredName.replace(\"/\", \"_\");\n\t\tString filename;\n\t\tfor (int i = 0; i < 100; i++) {\n\t\t\tif (i == 0) {\n\t\t\t\tfilename = desiredName + \".org\";\n\t\t\t} else {\n\t\t\t\tfilename = desiredName + i + \".org\";\n\t\t\t}\n\t\t\tFile f = new File(ORG_DIR, filename);\n\t\t\tif (!f.exists()) {\n\t\t\t\treturn new OrgFile(orgParser, filename);\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Filename not accessible\");\n\t}\n\n\t/**\n\t * Replaces the file on the remote end with the given content. It needs the org file to have\n\t * write permission, so \"r\" is wrong but \"rw\" is fine\n\t *\n\t * @param orgFile The file to save. Uses the filename stored in the object.\n\t */\n\t@Override\n\tpublic void putRemoteFile(OrgFile orgFile) throws IOException {\n\t\tfinal String orgfname = orgFile.getFilename();\n\t\tfinal File file = new File(ORG_DIR, orgfname);\n\t\ttry {\n\t\t\tfinal BufferedWriter bw = new BufferedWriter(new FileWriter(file));\n\t\t\tbw.write(orgFile.treeToString());\n\t\t\tbw.close();\n\t\t} catch (FileNotFoundException e) {\n\t\t\t// if you upload an org file with android studio's \"device file explorer\" tool,\n\t\t\t// it will be in readonly mode (only \"r\"), but we need it to be (also) in write\n\t\t\t// mode (so at least \"rw\"), because sync is 2-way. Amaze file manager can show\n\t\t\t// this property of files.\n\t\t\tNnnLogger.warning(SDSynchronizer.class, \"Read-only files like \"\n\t\t\t\t\t+ orgfname + \" are not supported! Please set this file as \" +\n\t\t\t\t\t\"writable. If you don't know how, just delete it and replace it by moving \" +\n\t\t\t\t\t\"it with an android file-manager app, which will probably fix it. This \" +\n\t\t\t\t\t\"caused the following exception:\");\n\t\t\tNnnLogger.exception(e);\n\t\t\tString msg = context.getString(R.string.unsupported_readonly_file, orgfname);\n\t\t\tToast.makeText(this.context, msg, Toast.LENGTH_SHORT).show();\n\t\t}\n\t}\n\n\t/**\n\t * Delete the file on the remote end.\n\t *\n\t * @param orgFile The file to delete.\n\t */\n\t@Override\n\tpublic void deleteRemoteFile(OrgFile orgFile) {\n\t\tif (orgFile != null && orgFile.getFilename() != null) {\n\t\t\tfinal File file = new File(ORG_DIR, orgFile.getFilename());\n\t\t\tFileHelper.tryDeleteFile(file, context);\n\t\t}\n\t}\n\n\t/**\n\t * Rename the file on the remote end.\n\t *\n\t * @param oldName The name it is currently stored as on the remote end.\n\t */\n\t@Override\n\tpublic void renameRemoteFile(String oldName, OrgFile orgFile) {\n\t\tif (orgFile == null || orgFile.getFilename() == null) {\n\t\t\tthrow new NullPointerException(\"No new filename\");\n\t\t}\n\t\tfinal File oldFile = new File(ORG_DIR, oldName);\n\t\tfinal File newFile = new File(ORG_DIR, orgFile.getFilename());\n\t\toldFile.renameTo(newFile);\n\t}\n\n\t/**\n\t * Returns a BufferedReader to the remote file. Null if it doesn't exist.\n\t *\n\t * @param filename Name of the file, without path\n\t */\n\t@Override\n\tpublic BufferedReader getRemoteFile(String filename) {\n\t\tfinal File file = new File(ORG_DIR, filename);\n\t\tBufferedReader br = null;\n\t\tif (file.exists()) {\n\t\t\ttry {\n\t\t\t\tbr = new BufferedReader(new FileReader(file));\n\t\t\t} catch (FileNotFoundException ignored) {\n\t\t\t\t// br remains = null;\n\t\t\t}\n\t\t}\n\t\treturn br;\n\t}\n\n\t/**\n\t * @return a set of all remote files.\n\t */\n\t@SuppressLint(\"DefaultLocale\")\n\t@Override\n\tpublic HashSet<String> getRemoteFilenames() {\n\t\tfinal HashSet<String> filenames = new HashSet<>();\n\t\tfinal File dir = new File(ORG_DIR);\n\t\tfinal File[] files = dir.listFiles((dir1, name) -> name.toLowerCase().endsWith(\".org\"));\n\n\t\tif (files != null) {\n\t\t\tfor (File f : files) {\n\t\t\t\tfilenames.add(f.getName());\n\t\t\t}\n\t\t}\n\n\t\treturn filenames;\n\t}\n\n\t/**\n\t * Use this to disconnect from any services and cleanup.\n\t */\n\t@Override\n\tpublic void postSynchronize() {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic Monitor getMonitor() {\n\t\treturn new FileWatcher(ORG_DIR);\n\t}\n\n\tpublic static class FileWatcher extends FileObserver implements Monitor {\n\n\t\tpublic OrgSyncService.SyncHandler handler;\n\n\n\t\tpublic FileWatcher(String path) {\n\t\t\tsuper(path, FileObserver.CREATE | FileObserver.DELETE\n\t\t\t\t\t| FileObserver.DELETE_SELF | FileObserver.MODIFY\n\t\t\t\t\t| FileObserver.MOVE_SELF | FileObserver.MOVED_FROM\n\t\t\t\t\t| FileObserver.MOVED_TO);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEvent(int event, String path) {\n\t\t\tif (handler != null) {\n\t\t\t\thandler.onMonitorChange();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void startMonitor(final OrgSyncService.SyncHandler handler) {\n\t\t\tthis.handler = handler;\n\t\t\tstartWatching();\n\t\t}\n\n\t\t@Override\n\t\tpublic void pauseMonitor() {\n\t\t\tstopWatching();\n\t\t\thandler = null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void terminate() {\n\t\t\tstopWatching();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/Synchronizer.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.content.Context;\nimport android.util.Pair;\n\nimport com.nononsenseapps.notepad.database.RemoteTask;\nimport com.nononsenseapps.notepad.database.RemoteTaskList;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\n\nimport org.cowboyprogrammer.org.OrgFile;\nimport org.cowboyprogrammer.org.OrgNode;\nimport org.cowboyprogrammer.org.parser.RegexParser;\n\nimport java.io.IOException;\nimport java.text.ParseException;\nimport java.util.Calendar;\nimport java.util.List;\nimport java.util.Objects;\n\npublic abstract class Synchronizer extends DBSyncBase implements SynchronizerInterface {\n\n\tpublic static final int SAVENONE = 0x0;\n\tpublic static final int SAVEDB = 0x01;\n\tpublic static final int SAVEORG = 0x10;\n\tpublic static final String TAG = \"OrgSynchronizer\";\n\n\tpublic Synchronizer(Context context) {\n\t\tsuper(context);\n\t}\n\n\t/**\n\t * Performs a full 2-way sync between the DB and the remote source.\n\t */\n\tpublic void fullSync() throws IOException, ParseException {\n\t\t// For all pairs of files and db entries\n\t\tfinal List<Pair<OrgFile, Pair<RemoteTaskList, TaskList>>> pairs = getFilesAndDBEntries();\n\n\t\tfor (Pair<OrgFile, Pair<RemoteTaskList, TaskList>> pair : pairs) {\n\t\t\tOrgFile file = pair.first;\n\t\t\tRemoteTaskList dbEntry = pair.second.first;\n\t\t\tTaskList list = pair.second.second;\n\t\t\tif (dbEntry == null) {\n\t\t\t\tif (file == null) {\n\t\t\t\t\t// NEW CREATE FILE\n\t\t\t\t\t// Create file\n\t\t\t\t\tfile = getNewFile(list.title);\n\t\t\t\t\tOrgConverter.toFileFromList(list, file);\n\n\t\t\t\t\t// Add tasks to File\n\t\t\t\t\tsyncTasks(context, list, file);\n\n\t\t\t\t\t// Save file\n\t\t\t\t\tputRemoteFile(file);\n\n\t\t\t\t\t// If name was not available, rename list as well\n\t\t\t\t\tif (!file.getFilename().equals(OrgConverter\n\t\t\t\t\t\t\t.getTitleAsFilename(list))) {\n\t\t\t\t\t\tlist.title = file.getFilename().substring(0,\n\t\t\t\t\t\t\t\tfile.getFilename().length() - 4);\n\t\t\t\t\t\tlist.save(context);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create DbEntry\n\t\t\t\t\tdbEntry = new RemoteTaskList();\n\t\t\t\t\tdbEntry.dbid = list._id;\n\t\t\t\t\tdbEntry.account = getAccountName();\n\t\t\t\t\tdbEntry.service = getServiceName();\n\t\t\t\t\tOrgConverter.toRemoteFromFile(dbEntry, file);\n\t\t\t\t\tdbEntry.save(context);\n\n\t\t\t\t} else {\n\t\t\t\t\t// NEW CREATE DB LIST\n\t\t\t\t\t// Create TaskList\n\t\t\t\t\tlist = new TaskList();\n\t\t\t\t\tOrgConverter.toListFromFile(list, file);\n\t\t\t\t\tlist.save(context, file.lastModified());\n\n\t\t\t\t\t// Create DbEntry\n\t\t\t\t\tdbEntry = new RemoteTaskList();\n\t\t\t\t\tdbEntry.dbid = list._id;\n\t\t\t\t\tdbEntry.account = getAccountName();\n\t\t\t\t\tdbEntry.service = getServiceName();\n\t\t\t\t\tOrgConverter.toRemoteFromFile(dbEntry, file);\n\t\t\t\t\tdbEntry.save(context);\n\n\t\t\t\t\t// Now do the tasks\n\t\t\t\t\tif (syncTasks(context, list, file)) {\n\t\t\t\t\t\t// Something changed in the file.\n\t\t\t\t\t\tputRemoteFile(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (list == null) {\n\t\t\t\t\t// DELETE FILE DB\n\t\t\t\t\tdeleteRemoteFile(file);\n\t\t\t\t\tdeleteLocal(/*list=*/null, dbEntry);\n\t\t\t\t} else {\n\t\t\t\t\tif (file == null) {\n\t\t\t\t\t\t// DELETE DB LIST\n\t\t\t\t\t\t// List and entry\n\t\t\t\t\t\tdeleteLocal(list, dbEntry);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// UPDATE EXISTING LIST, IF CHANGED\n\t\t\t\t\t\tboolean shouldSaveFile = false;\n\n\t\t\t\t\t\tif (wasRenamed(list, file)) {\n\t\t\t\t\t\t\tfinal String oldName = file.getFilename();\n\t\t\t\t\t\t\trenameFile(list, dbEntry, file);\n\t\t\t\t\t\t\trenameRemoteFile(oldName, file);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Merge information in database and file\n\t\t\t\t\t\tfinal int shouldSave = merge(list, dbEntry, file);\n\n\t\t\t\t\t\tif (0 < (shouldSave & SAVEORG)) {\n\t\t\t\t\t\t\t// UPDATE FILE DB\n\t\t\t\t\t\t\tshouldSaveFile = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (0 < (shouldSave & SAVEDB)) {\n\t\t\t\t\t\t\t// UPDATE LIST DB\n\t\t\t\t\t\t\tlist.save(context);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (shouldSave != SAVENONE) {\n\t\t\t\t\t\t\tOrgConverter.toRemoteFromFile(dbEntry, file);\n\t\t\t\t\t\t\tdbEntry.updated = Calendar.getInstance()\n\t\t\t\t\t\t\t\t\t.getTimeInMillis();\n\t\t\t\t\t\t\tdbEntry.save(context);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// In both cases, sync tasks\n\t\t\t\t\t\tif (syncTasks(context, list, file) || shouldSaveFile) {\n\t\t\t\t\t\t\t// Something changed in the file.\n\t\t\t\t\t\t\tputRemoteFile(file);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Merge the list and file. Fields considered are the listtype and\n\t * listsorting which are stored as comments in the file.\n\t *\n\t * @return an integer denoting which should be saved. 0 for none, 0x01 for\n\t * task, 0x10 for node. 0x11 for both.\n\t */\n\tprivate int merge(final TaskList list, final RemoteTaskList dbEntry, final OrgFile file) {\n\t\tint shouldSave = SAVENONE;\n\n\t\tshouldSave |= mergeSorting(list, dbEntry, file);\n\t\tshouldSave |= mergeListType(list, dbEntry, file);\n\n\t\treturn shouldSave;\n\t}\n\n\tprivate int mergeSorting(final TaskList list, final RemoteTaskList dbEntry,\n\t\t\t\t\t\t\t final OrgFile file) {\n\t\tfinal int shouldSave;\n\t\tfinal String filesorting = OrgConverter.getListSortingFromMeta(file);\n\t\tif (list.sorting == null\n\t\t\t\t&& RemoteTaskListFile.getSorting(dbEntry) != null\n\t\t\t\t|| list.sorting != null\n\t\t\t\t&& !list.sorting.equals(RemoteTaskListFile.getSorting(dbEntry))) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tOrgConverter.setSortingOnFile(list, file);\n\t\t} else if (filesorting == null\n\t\t\t\t&& RemoteTaskListFile.getSorting(dbEntry) != null\n\t\t\t\t|| filesorting != null\n\t\t\t\t&& !filesorting.equals(RemoteTaskListFile.getSorting(dbEntry))) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tlist.sorting = filesorting;\n\t\t} else {\n\t\t\tshouldSave = SAVENONE;\n\t\t}\n\t\treturn shouldSave;\n\t}\n\n\tprivate int mergeListType(final TaskList list, final RemoteTaskList dbEntry,\n\t\t\t\t\t\t\t  final OrgFile file) {\n\t\tfinal int shouldSave;\n\t\tfinal String filelisttype = OrgConverter.getListTypeFromMeta(file);\n\t\tif (list.listtype == null\n\t\t\t\t&& RemoteTaskListFile.getListType(dbEntry) != null\n\t\t\t\t|| list.listtype != null\n\t\t\t\t&& !list.listtype.equals(RemoteTaskListFile.getListType(dbEntry))) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tOrgConverter.setListTypeOnFile(list, file);\n\t\t} else if (filelisttype == null\n\t\t\t\t&& RemoteTaskListFile.getListType(dbEntry) != null\n\t\t\t\t|| filelisttype != null\n\t\t\t\t&& !filelisttype.equals(RemoteTaskListFile.getListType(dbEntry))) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tlist.listtype = filelisttype;\n\t\t} else {\n\t\t\tshouldSave = SAVENONE;\n\t\t}\n\t\treturn shouldSave;\n\t}\n\n\tprivate boolean syncTasks(final Context context, final TaskList list, final OrgFile file) {\n\t\tfinal List<Pair<OrgNode, Pair<RemoteTask, Task>>> pairs = getNodesAndDBEntries(file, list);\n\t\tboolean shouldUpdateFile = false;\n\n\t\tOrgNode prevNode = null;\n\n\t\tfor (Pair<OrgNode, Pair<RemoteTask, Task>> pair : pairs) {\n\t\t\tOrgNode node = pair.first;\n\t\t\tRemoteTask dbEntry = pair.second.first;\n\t\t\tTask task = pair.second.second;\n\n\t\t\tif (dbEntry == null) {\n\t\t\t\tif (node == null) {\n\t\t\t\t\t// CREATE NODE DB\n\t\t\t\t\t//Log.d(TAG, \"CREATE NODE DB\");\n\t\t\t\t\tnode = new OrgNode(new RegexParser());\n\t\t\t\t\tnode.setLevel(1);\n\t\t\t\t\tnode.setParent(file);\n\t\t\t\t\tint idx = -1;\n\t\t\t\t\tif (prevNode != null) {\n\t\t\t\t\t\tidx = file.getSubNodes().indexOf(prevNode);\n\t\t\t\t\t}\n\t\t\t\t\tfile.getSubNodes().add(idx + 1, node);\n\t\t\t\t\tOrgConverter.toNodeFromTask(task, node);\n\n\t\t\t\t\tdbEntry = new RemoteTask();\n\t\t\t\t\tdbEntry.dbid = task._id;\n\t\t\t\t\tdbEntry.listdbid = list._id;\n\t\t\t\t\tdbEntry.account = getAccountName();\n\t\t\t\t\tdbEntry.service = getServiceName();\n\t\t\t\t\tOrgConverter.toRemoteFromNode(dbEntry, node);\n\t\t\t\t\tdbEntry.save(context);\n\n\t\t\t\t\tshouldUpdateFile = true;\n\t\t\t\t} else {\n\t\t\t\t\t// CREATE TASK DB\n\t\t\t\t\t//Log.d(TAG, \"CREATE TASK DB\");\n\t\t\t\t\ttask = new Task();\n\t\t\t\t\ttask.dblist = list._id;\n\t\t\t\t\tOrgConverter.toTaskFromNode(task, node);\n\t\t\t\t\ttask.save(context);\n\n\t\t\t\t\tdbEntry = new RemoteTask();\n\t\t\t\t\tdbEntry.dbid = task._id;\n\t\t\t\t\tdbEntry.listdbid = list._id;\n\t\t\t\t\tdbEntry.account = getAccountName();\n\t\t\t\t\tdbEntry.service = getServiceName();\n\t\t\t\t\tshouldUpdateFile = OrgConverter.toRemoteFromNode(dbEntry, node);\n\t\t\t\t\tdbEntry.save(context);\n\n\t\t\t\t\treplaceNotifications(task, node);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (task == null) {\n\t\t\t\t\t// DELETE NODE DB\n\t\t\t\t\t//Log.d(TAG, \"DELETE NODE DB\");\n\t\t\t\t\tdeleteLocal(/*task=*/null, dbEntry);\n\t\t\t\t\tif (node != null) {\n\t\t\t\t\t\tdeleteNode(node);\n\t\t\t\t\t\tshouldUpdateFile = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (node == null) {\n\t\t\t\t\t\t// DELETE DB TASK\n\t\t\t\t\t\t//Log.d(TAG, \"DELETE TASK DB\");\n\t\t\t\t\t\tdeleteLocal(task, dbEntry);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// TODO need to check notifications also\n\t\t\t\t\t\t//Log.d(TAG, \"MERGE TASKS\");\n\t\t\t\t\t\tfinal int shouldSave = merge(task, dbEntry, node);\n\n\t\t\t\t\t\tif (0 < (shouldSave & SAVEORG)) {\n\t\t\t\t\t\t\t// UPDATE NODE DB\n\t\t\t\t\t\t\tOrgConverter.toNodeFromRemote(node, dbEntry);\n\t\t\t\t\t\t\tshouldUpdateFile = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (0 < (shouldSave & SAVEDB)) {\n\t\t\t\t\t\t\ttask.save(context);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (0 < shouldSave) {\n\t\t\t\t\t\t\t// Remember this version for later\n\t\t\t\t\t\t\tOrgConverter.toRemoteFromNode(dbEntry, node);\n\t\t\t\t\t\t\tdbEntry.save(context);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remember the previous next time for positioning\n\t\t\tif (node != null) {\n\t\t\t\tprevNode = node;\n\t\t\t}\n\t\t}\n\n\t\treturn shouldUpdateFile;\n\t}\n\n\t/**\n\t * @param node to delete from the tree structure. Preserves sub nodes.\n\t */\n\tprivate void deleteNode(final OrgNode node) {\n\t\tfinal OrgNode parent = node.getParent();\n\t\t// If no parent, nothing to do\n\t\tif (parent == null)\n\t\t\treturn;\n\n\t\t// If sub nodes, transfer to root\n\t\tif (!node.getSubNodes().isEmpty()) {\n\t\t\tfinal int i = parent.getSubNodes().indexOf(node);\n\t\t\tparent.getSubNodes().addAll(i, node.getSubNodes());\n\t\t}\n\t\t// Remove the node\n\t\tparent.getSubNodes().remove(node);\n\t}\n\n\t/**\n\t * Merges the task and node. The fields considered are title, body,\n\t * completed and deadline.\n\t *\n\t * @return an integer denoting which should be saved. 0 for none, 0x01 for\n\t * task, 0x10 for node. 0x11 for both.\n\t */\n\tprotected int merge(final Task task, final RemoteTask remote, final OrgNode node) {\n\t\tif (task == null || remote == null || node == null) {\n\t\t\tthrow new NullPointerException(\"A merge operation can't have null parties!\");\n\t\t}\n\t\t// 0x01 if task should be saved\n\t\t// 0x10 if node should be saved\n\t\t// 0x11 if both should be saved\n\t\t// 0x00 if nothing needs to be saved\n\t\tint shouldSave = SAVENONE;\n\n\t\tshouldSave |= mergeTitles(task, remote, node);\n\t\tshouldSave |= mergeBodies(task, remote, node);\n\t\tshouldSave |= mergeTodo(task, remote, node);\n\t\tshouldSave |= mergeTimestamps(task, remote, node);\n\n\t\treturn shouldSave;\n\t}\n\n\tprivate int mergeTodo(final Task task, final RemoteTask remote, final OrgNode node) {\n\t\tfinal int shouldSave;\n\t\tfinal String taskTodo;\n\t\tif (task.completed != null)\n\t\t\ttaskTodo = \"DONE\";\n\t\telse\n\t\t\ttaskTodo = \"TODO\";\n\n\t\tif (!taskTodo.equals(RemoteTaskNode.getTodo(remote))) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tnode.setTodo(taskTodo);\n\t\t} else if (RemoteTaskNode.getTodo(remote) != null\n\t\t\t\t&& !RemoteTaskNode.getTodo(remote).equals(node.getTodo())) {\n\t\t\tshouldSave = SAVEDB;\n\t\t\tif (\"DONE\".equals(node.getTodo())) {\n\t\t\t\ttask.completed = Calendar.getInstance().getTimeInMillis();\n\t\t\t} else {\n\t\t\t\ttask.completed = null;\n\t\t\t}\n\t\t} else {\n\t\t\tshouldSave = SAVENONE;\n\t\t}\n\n\t\treturn shouldSave;\n\t}\n\n\tprivate int mergeTimestamps(final Task task, final RemoteTask remote, final OrgNode node) {\n\t\tfinal int shouldSave;\n\t\tLong basedue = null;\n\t\tif (RemoteTaskNode.getDueTime(remote) != null\n\t\t\t\t&& !RemoteTaskNode.getDueTime(remote).isEmpty()) {\n\t\t\tbasedue = Long.parseLong(RemoteTaskNode.getDueTime(remote));\n\t\t}\n\n\t\tfinal Long nodedue = OrgConverter.getDeadline(node);\n\n\t\tif (!Objects.equals(task.due, basedue)) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tOrgConverter.setDeadline(node, task.due);\n\t\t} else if (!Objects.equals(nodedue, basedue)) {\n\t\t\tshouldSave = SAVEDB;\n\t\t\ttask.due = nodedue;\n\t\t} else {\n\t\t\tshouldSave = SAVENONE;\n\t\t}\n\n\t\treturn shouldSave;\n\t}\n\n\tprivate int mergeBodies(final Task task, final RemoteTask remote, final OrgNode node) {\n\t\tfinal int shouldSave;\n\t\tboolean taskChanged = !task.note.equals(RemoteTaskNode.getBody(remote));\n\t\t// Check with trailing newline also\n\t\tif (taskChanged) {\n\t\t\ttaskChanged = !(task.note + \"\\n\").equals(RemoteTaskNode.getBody(remote));\n\t\t}\n\n\t\tif (taskChanged) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tnode.setBody(task.note);\n\t\t} else if (!node.getBody().equals(RemoteTaskNode.getBody(remote))) {\n\t\t\tshouldSave = SAVEDB;\n\t\t\ttask.note = node.getBody();\n\t\t\t/*\n\t\t\t * It's not possible to differentiate if the user added a trailing\n\t\t\t * newline or the sync logic did. I will assume that the sync logic did.\n\t\t\t */\n\t\t\tif (task.note != null && task.note.endsWith(\"\\n\")) {\n\t\t\t\ttask.note = task.note.substring(0, task.note.length() - 1);\n\t\t\t}\n\t\t} else {\n\t\t\tshouldSave = SAVENONE;\n\t\t}\n\n\t\treturn shouldSave;\n\t}\n\n\tprivate int mergeTitles(final Task task, final RemoteTask remote, final OrgNode node) {\n\t\tfinal int shouldSave;\n\t\tif (!task.title.equals(RemoteTaskNode.getTitle(remote))) {\n\t\t\tshouldSave = SAVEORG;\n\t\t\tnode.setTitle(task.title);\n\t\t} else if (!node.getTitle().equals(RemoteTaskNode.getTitle(remote))) {\n\t\t\tshouldSave = SAVEDB;\n\t\t\ttask.title = node.getTitle();\n\t\t} else {\n\t\t\tshouldSave = SAVENONE;\n\t\t}\n\t\treturn shouldSave;\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/SynchronizerInterface.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.sync.orgsync;\n\nimport org.cowboyprogrammer.org.OrgFile;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.text.ParseException;\nimport java.util.HashSet;\n\n/**\n * This interface defines an Org-Mode synchronizer.\n */\npublic interface SynchronizerInterface {\n\n\t/**\n\t * @return A unique name for this service. Should be descriptive, like\n\t * SDOrg or SSHOrg.\n\t */\n\tString getServiceName();\n\n\t/**\n\t * @return The username of the configured service. Likely an e-mail.\n\t */\n\tString getAccountName();\n\n\t/**\n\t * Returns true if the synchronizer has been configured. This is called\n\t * before synchronization. It will be true if the user has selected an\n\t * account, folder etc...\n\t */\n\tboolean isConfigured();\n\n\t/**\n\t * Returns an OrgFile object with a filename set that is guaranteed to\n\t * not already exist. Use this method to avoid having multiple objects\n\t * pointing to the same file.\n\t *\n\t * @param desiredName The name you'd want. If it exists,\n\t *                    it will be used as the base in desiredName1,\n\t *                    desiredName2, etc. Limited to 99.\n\t * @return an OrgFile guaranteed not to exist.\n\t */\n\tOrgFile getNewFile(final String desiredName) throws IOException, IllegalArgumentException;\n\n\t/**\n\t * Replaces the file on the remote end with the given content.\n\t *\n\t * @param orgFile The file to save. Uses the filename stored in the object.\n\t */\n\tvoid putRemoteFile(final OrgFile orgFile) throws IOException;\n\n\t/**\n\t * Delete the file on the remote end.\n\t *\n\t * @param orgFile The file to delete.\n\t */\n\tvoid deleteRemoteFile(final OrgFile orgFile) throws IOException;\n\n\t/**\n\t * Rename the file on the remote end.\n\t *\n\t * @param oldName The name it is currently stored as on the remote end.\n\t * @param orgFile This contains the new name.\n\t */\n\tvoid renameRemoteFile(final String oldName, final OrgFile orgFile) throws IOException;\n\n\t/**\n\t * Returns a BufferedReader to the remote file. Null if it doesn't exist.\n\t *\n\t * @param filename Name of the file, without path\n\t */\n\tBufferedReader getRemoteFile(final String filename) throws IOException;\n\n\t/**\n\t * @return a set of all remote files.\n\t */\n\tHashSet<String> getRemoteFilenames() throws IOException;\n\n\t/**\n\t * Do a full 2-way sync.\n\t */\n\tvoid fullSync() throws IOException, ParseException;\n\n\t/**\n\t * Use this to disconnect from any services and cleanup.\n\t */\n\tvoid postSynchronize();\n\n\t/**\n\t * @return a Monitor for this source. May be null.\n\t */\n\tMonitor getMonitor();\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetConfig.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.widget.list;\n\n\nimport android.appwidget.AppWidgetManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Adapter;\nimport android.widget.AdapterView;\nimport android.widget.AdapterView.OnItemSelectedListener;\nimport android.widget.ImageButton;\nimport android.widget.SeekBar;\nimport android.widget.SeekBar.OnSeekBarChangeListener;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.content.ContextCompat;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter;\nimport androidx.cursoradapter.widget.SimpleCursorAdapter.ViewBinder;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.app.LoaderManager.LoaderCallbacks;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.ThemeHelper;\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.ActivityWidgetConfigBinding;\nimport com.nononsenseapps.ui.ExtrasCursorAdapter;\nimport com.nononsenseapps.ui.TitleNoteTextView;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n/**\n * The activity where the user can configure the \"list widget\". It also shows a preview!\n */\npublic class ListWidgetConfig extends AppCompatActivity {\n\n\tpublic static final String KEY_LIST = \"widget1_key_list\";\n\tpublic static final String KEY_LIST_TITLE = \"widget1_key_list_title\";\n\tpublic static final String KEY_SORT_TYPE = \"widget1_key_sort_type\";\n\n\tpublic static final String KEY_THEME = \"widget1_key_current_theme\";\n\tpublic static final String KEY_TEXTPRIMARY = \"widget1_key_primary_text\";\n\tpublic static final String KEY_TEXTSECONDARY = \"widget1_key_secondary_text\";\n\n\tpublic static final String KEY_HIDDENHEADER = \"widget1_key_hiddenheader\";\n\tpublic static final String KEY_SHADE_COLOR = \"widget1_key_shadecolor\";\n\n\tpublic static final String KEY_HIDDENDATE = \"widget1_key_hiddendate\";\n\tpublic static final String KEY_HIDDENCHECKBOX = \"widget1_key_hiddencheckbox\";\n\tpublic static final String KEY_TITLEROWS = \"widget1_key_titlerows\";\n\n\t/**\n\t * If the widget should show completed notes instead of hiding them\n\t */\n\tpublic static final String KEY_SHOWCOMPLETED = \"widget1_key_showcompleted\";\n\n\t/**\n\t * Used in widget service/provider\n\t */\n\tpublic static final String KEY_LOCKSCREEN = \"widget1_key_lockscreen\";\n\n\tpublic final static int THEME_DARK = 0;\n\tpublic final static int THEME_LIGHT = 1;\n\n\t// These are the default widget values\n\tpublic final static int DEFAULT_THEME = THEME_DARK;\n\t// 75% translucent black\n\tpublic final static int DEFAULT_SHADE = 0xC0000000;\n\t// White (android primary dark)\n\tpublic final static int DEFAULT_TEXTPRIMARY = 0xff000000;\n\t// Greyish (android secondary dark)\n\tpublic final static int DEFAULT_TEXTSECONDARY = 0xffbebebe;\n\t// Number of rows\n\tpublic final static int DEFAULT_ROWS = 3;\n\t// All lists id\n\tpublic final static int ALL_LISTS_ID = -2;\n\n\tprivate int appWidgetId;\n\tprivate SimpleWidgetPreviewAdapter mNotesAdapter;\n\tprivate LoaderCallbacks<Cursor> mCallback;\n\tprivate ExtrasCursorAdapter mListAdapter;\n\n\t/**\n\t * for {@link R.layout#activity_widget_config}\n\t */\n\tprivate ActivityWidgetConfigBinding mBinding;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tmBinding = ActivityWidgetConfigBinding.inflate(getLayoutInflater());\n\t\tsetContentView(mBinding.getRoot());\n\n\t\tsetResult(RESULT_CANCELED);\n\n\t\tIntent intent = getIntent();\n\t\tif (intent != null && intent.getExtras() != null) {\n\t\t\tappWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,\n\t\t\t\t\tAppWidgetManager.INVALID_APPWIDGET_ID);\n\t\t} else {\n\t\t\tappWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;\n\t\t\tNnnLogger.debug(ListWidgetConfig.class, \"Invalid ID given in the intent\");\n\t\t\tIntent resultValue = new Intent();\n\t\t\tresultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,\n\t\t\t\t\tappWidgetId);\n\t\t\tsetResult(RESULT_CANCELED, resultValue);\n\t\t\tfinish();\n\t\t}\n\n\t\tsetupPreview();\n\t\tsetupActionBar();\n\t\tsetupConfig();\n\t}\n\n\tvoid setupPreview() {\n\t\tfinal WidgetPrefs widgetPrefs = new WidgetPrefs(this, appWidgetId);\n\n\t\tmNotesAdapter = new SimpleWidgetPreviewAdapter(this,\n\t\t\t\tR.layout.widgetlist_item, R.layout.widgetlist_header, null,\n\t\t\t\tnew String[] { Task.Columns.TITLE, Task.Columns.DUE,\n\t\t\t\t\t\tTask.Columns.COMPLETED, Task.Columns.COMPLETED,\n\t\t\t\t\t\tTask.Columns.COMPLETED }, new int[] {\n\t\t\t\tandroid.R.id.text1, R.id.dueDate,\n\t\t\t\tR.id.completedCheckBoxDark, R.id.itemSpacer,\n\t\t\t\tR.id.completedCheckBoxLight }, 0);\n\t\tmNotesAdapter.setViewBinder(new ViewBinder() {\n\n\t\t\tfinal WidgetPrefs widgetPrefs = new WidgetPrefs(ListWidgetConfig.this, appWidgetId);\n\t\t\tboolean isHeader = false;\n\t\t\tString sTemp = \"\";\n\t\t\tfinal SimpleDateFormat dateFormatter = TimeFormatter\n\t\t\t\t\t.getLocalFormatterMicro(ListWidgetConfig.this);\n\n\t\t\t@Override\n\t\t\tpublic boolean setViewValue(View view, Cursor c, int colIndex) {\n\t\t\t\t// Check for headers, they have invalid ids\n\t\t\t\tisHeader = c.getLong(0) == -1;\n\t\t\t\tswitch (colIndex) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\t// title\n\t\t\t\t\t\tif (isHeader) {\n\t\t\t\t\t\t\tsTemp = c.getString(1);\n\t\t\t\t\t\t\tlong dueDateMillis = c.getLong(4);\n\t\t\t\t\t\t\tsTemp = Task.getHeaderNameForListSortedByDate(sTemp, dueDateMillis,\n\t\t\t\t\t\t\t\t\tListWidgetConfig.this);\n\t\t\t\t\t\t\t((TextView) view).setText(sTemp);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t((TextView) view).setText(TitleNoteTextView.getStyledText(\n\t\t\t\t\t\t\t\t\tc.getString(1), c.getString(2),\n\t\t\t\t\t\t\t\t\t1.0f, 1, 1));\n\t\t\t\t\t\t\tfinal int rows = widgetPrefs.getInt(KEY_TITLEROWS, DEFAULT_ROWS);\n\t\t\t\t\t\t\t((TextView) view).setMaxLines(Math.max(rows, 1));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Set color\n\t\t\t\t\t\t((TextView) view).setTextColor(\n\t\t\t\t\t\t\t\twidgetPrefs.getInt(KEY_TEXTPRIMARY, DEFAULT_TEXTPRIMARY));\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\t// already done.\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\t// Complete checkbox => decide if it will be visible\n\t\t\t\t\t\tboolean visible;\n\n\t\t\t\t\t\t// see widgetlist_item.xml\n\t\t\t\t\t\tif (view.getId() == R.id.completedCheckBoxLight) {\n\t\t\t\t\t\t\tassert view instanceof ImageButton;\n\t\t\t\t\t\t\t// show one of the 2 imagebuttons depending on the chosen theme\n\t\t\t\t\t\t\tvisible = THEME_LIGHT == widgetPrefs.getInt(KEY_THEME, DEFAULT_THEME);\n\n\t\t\t\t\t\t\t// > 0 if it user completed the task, 0 otherwise\n\t\t\t\t\t\t\tlong millisOfCompletion = c.getLong(3);\n\t\t\t\t\t\t\tif (millisOfCompletion > 0) {\n\t\t\t\t\t\t\t\t// this is an imageview, not a checkbox. I change its state\n\t\t\t\t\t\t\t\t// by swapping the image source\n\t\t\t\t\t\t\t\t((ImageButton) view).setImageResource(R.drawable.ic_checkbox_checked);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t((ImageButton) view).setImageResource(R.drawable.ic_checkbox_unchecked);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// set the color of the checkbox: use the system's accent color\n\t\t\t\t\t\t\tint color = ThemeHelper.getThemeAccentColor(ListWidgetConfig.this);\n\t\t\t\t\t\t\t((ImageButton) view).setColorFilter(color);\n\t\t\t\t\t\t} else if (view.getId() == R.id.completedCheckBoxDark) {\n\t\t\t\t\t\t\tassert view instanceof ImageButton;\n\t\t\t\t\t\t\tvisible = THEME_DARK == widgetPrefs.getInt(KEY_THEME, DEFAULT_THEME);\n\n\t\t\t\t\t\t\t// see above\n\t\t\t\t\t\t\tlong millisOfCompletion = c.getLong(3);\n\t\t\t\t\t\t\tif (millisOfCompletion > 0) {\n\t\t\t\t\t\t\t\t((ImageButton) view).setImageResource(R.drawable.ic_checkbox_checked);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t((ImageButton) view).setImageResource(R.drawable.ic_checkbox_unchecked);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tint color = ThemeHelper.getThemeAccentColor(ListWidgetConfig.this);\n\t\t\t\t\t\t\t((ImageButton) view).setColorFilter(color);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Spacer\n\t\t\t\t\t\t\tassert view.getId() == R.id.itemSpacer;\n\t\t\t\t\t\t\tvisible = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvisible &= !widgetPrefs.getBoolean(KEY_HIDDENCHECKBOX, false);\n\t\t\t\t\t\tview.setVisibility(visible ? View.VISIBLE : View.GONE);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\t// Date\n\t\t\t\t\t\tview.setVisibility(widgetPrefs.getBoolean(KEY_HIDDENDATE, false)\n\t\t\t\t\t\t\t\t? View.GONE : View.VISIBLE);\n\t\t\t\t\t\tif (c.isNull(colIndex)) {\n\t\t\t\t\t\t\t((TextView) view).setText(\"\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t((TextView) view).setText(\n\t\t\t\t\t\t\t\t\tdateFormatter.format(new Date(c.getLong(colIndex))));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t((TextView) view).setTextColor(\n\t\t\t\t\t\t\t\twidgetPrefs.getInt(KEY_TEXTPRIMARY, DEFAULT_TEXTPRIMARY));\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\n\t\t\t}\n\t\t});\n\n\t\tmBinding.widgetPreviewWrapper.widgetPreview.notesList.setAdapter(mNotesAdapter);\n\n\t\tmCallback = new LoaderCallbacks<>() {\n\n\t\t\t/**\n\t\t\t * This is for the preview of the widget that appears in the config activity,\n\t\t\t * but most of the code is taken from\n\t\t\t * {@link ListWidgetService.ListRemoteViewsFactory#onDataSetChanged()}\n\t\t\t */\n\t\t\t@NonNull\n\t\t\t@Override\n\t\t\tpublic Loader<Cursor> onCreateLoader(int id, Bundle arg1) {\n\t\t\t\tif (id == 1) {\n\t\t\t\t\treturn new CursorLoader(ListWidgetConfig.this,\n\t\t\t\t\t\t\tTaskList.URI, TaskList.Columns.FIELDS, null, null,\n\t\t\t\t\t\t\tgetString(R.string.const_as_alphabetic, TaskList.Columns.TITLE));\n\t\t\t\t}\n\n\t\t\t\t// TODO maybe ListWidgetService.onDataSetChanged() and this function share some\n\t\t\t\t//  common code. Try to understand and refactor it\n\n\t\t\t\tfinal Uri targetUri;\n\n\t\t\t\tfinal long listId = widgetPrefs.getLong(KEY_LIST, ALL_LISTS_ID);\n\t\t\t\tfinal String sortSpec;\n\t\t\t\tfinal String sortType = widgetPrefs\n\t\t\t\t\t\t.getString(KEY_SORT_TYPE, getString(R.string.default_sorttype));\n\t\t\t\tboolean isShowingCompleted = widgetPrefs\n\t\t\t\t\t\t.getBoolean(ListWidgetConfig.KEY_SHOWCOMPLETED, false);\n\n\t\t\t\tif (sortType.equals(getString(R.string.const_possubsort)) && listId > 0) {\n\t\t\t\t\ttargetUri = Task.URI;\n\t\t\t\t\tsortSpec = Task.Columns.LEFT;\n\t\t\t\t} else if (sortType.equals(getString(R.string.const_modified))) {\n\t\t\t\t\ttargetUri = Task.URI;\n\t\t\t\t\tsortSpec = Task.Columns.UPDATED + \" DESC\";\n\t\t\t\t} else if (sortType.equals(getString(R.string.const_duedate))) {\n\t\t\t\t\t// due date sorting\n\t\t\t\t\ttargetUri = Task.URI_SECTIONED_BY_DATE;\n\t\t\t\t\tsortSpec = null;\n\t\t\t\t} else {\n\t\t\t\t\t// Alphabetic\n\t\t\t\t\ttargetUri = Task.URI;\n\t\t\t\t\tsortSpec = getString(R.string.const_as_alphabetic, Task.Columns.TITLE);\n\t\t\t\t}\n\n\t\t\t\tString listWhere;\n\t\t\t\tString[] listArg;\n\t\t\t\tif (listId > 0) {\n\t\t\t\t\t// only get notes in that list id\n\t\t\t\t\tlistArg = new String[] { Long.toString(listId) };\n\n\t\t\t\t\t// if user does not want to also show completed tasks in widget, the query\n\t\t\t\t\t// will filter away database records with a \"completed\" unix time\n\t\t\t\t\tlistWhere = isShowingCompleted\n\t\t\t\t\t\t\t? \"CAST(\" + Task.Columns.DBLIST + \" AS INTEGER) IS ?\"\n\t\t\t\t\t\t\t: \"CAST(\" + Task.Columns.DBLIST + \" AS INTEGER) IS ? AND \" + Task.Columns.COMPLETED + \" IS NULL\";\n\t\t\t\t} else {\n\t\t\t\t\t// all list ids\n\t\t\t\t\tlistArg = null;\n\n\t\t\t\t\t// if user wants to show completed tasks, since here it shows from all lists,\n\t\t\t\t\t// then this \"where\" should show everything. In android logic, that means\n\t\t\t\t\t// sending \"null\" to CursorLoader() here below\n\t\t\t\t\tlistWhere = isShowingCompleted\n\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t: Task.Columns.COMPLETED + \" IS NULL\";\n\t\t\t\t}\n\n\t\t\t\treturn new CursorLoader(ListWidgetConfig.this, targetUri,\n\t\t\t\t\t\tTask.Columns.FIELDS, listWhere, listArg, sortSpec);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> l, Cursor c) {\n\t\t\t\tif (l.getId() == 1) {\n\t\t\t\t\tmListAdapter.swapCursor(c);\n\t\t\t\t\tfinal int pos = getListPositionOf(mListAdapter,\n\t\t\t\t\t\t\twidgetPrefs.getLong(KEY_LIST, ALL_LISTS_ID));\n\t\t\t\t\t//if (c.getCount() > 0) {\n\t\t\t\t\t// Set current item\n\t\t\t\t\tmBinding.widgetConfWrapper.listSpinner.setSelection(pos);\n\t\t\t\t\t//}\n\t\t\t\t} else {\n\t\t\t\t\tmNotesAdapter.swapCursor(c);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> l) {\n\t\t\t\tif (l.getId() == 1) {\n\t\t\t\t\tmListAdapter.swapCursor(null);\n\t\t\t\t} else {\n\t\t\t\t\tmNotesAdapter.swapCursor(null);\n\t\t\t\t}\n\t\t\t}\n\n\t\t};\n\t\tLoaderManager.getInstance(this).restartLoader(1, null, mCallback);\n\t}\n\n\t/**\n\t * Calling {@link SimpleWidgetPreviewAdapter#notifyDataSetChanged} on\n\t * {@link #mNotesAdapter} can only refresh the views, but it doesn't change the\n\t * cursor, so it can't toggle between \"do/dont show completed notes\". This function\n\t * can, because it updates the underlying cursor\n\t */\n\tvoid reloadTasks() {\n\t\tLoaderManager.getInstance(this).restartLoader(0, null, mCallback);\n\t}\n\n\tvoid setupActionBar() {\n\t\tfinal WidgetPrefs widgetPrefs = new WidgetPrefs(this, appWidgetId);\n\n\t\tLayoutInflater inflater = (LayoutInflater) getSupportActionBar()\n\t\t\t\t.getThemedContext()\n\t\t\t\t.getSystemService(LAYOUT_INFLATER_SERVICE);\n\t\tfinal View customActionBarView = inflater\n\t\t\t\t.inflate(R.layout.actionbar_custom_view_done, null);\n\n\t\tcustomActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(v -> {\n\t\t\t// \"Done\"\n\t\t\t// // Set success\n\t\t\twidgetPrefs.setPresent();\n\t\t\tIntent resultValue = new Intent();\n\t\t\tresultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);\n\t\t\tsetResult(RESULT_OK, resultValue);\n\t\t\t// Build/Update widget\n\t\t\tAppWidgetManager appWidgetManager = AppWidgetManager\n\t\t\t\t\t.getInstance(getApplicationContext());\n\t\t\t// Log.d(TAG, \"finishing WidgetId \" + appWidgetId);\n\t\t\tappWidgetManager.updateAppWidget(appWidgetId, ListWidgetProvider.buildRemoteViews(\n\t\t\t\t\tgetApplicationContext(), appWidgetManager, appWidgetId, widgetPrefs));\n\n\t\t\t// Update list items\n\t\t\tappWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.notesList);\n\t\t\t// Destroy activity\n\t\t\tfinish();\n\t\t});\n\t\t// Show the custom action bar view and hide the normal Home icon and title.\n\t\tgetSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,\n\t\t\t\tActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME |\n\t\t\t\t\t\tActionBar.DISPLAY_SHOW_TITLE);\n\t\tgetSupportActionBar().setCustomView(customActionBarView);\n\t}\n\n\tvoid setupConfig() {\n\t\tfinal WidgetPrefs widgetPrefs = new WidgetPrefs(this, appWidgetId);\n\n\t\tfinal String[] sortTypeValues = getResources()\n\t\t\t\t.getStringArray(R.array.sortingvalues_preference);\n\t\tfinal String[] themeValues = getResources()\n\t\t\t\t.getStringArray(R.array.widget_themevalues_preference);\n\n\t\tif (themeValues == null) {\n\t\t\tNnnLogger.debug(ListWidgetConfig.class, \"themevalues null\");\n\t\t} else {\n\t\t\tfor (String s : themeValues) {\n\t\t\t\tNnnLogger.debug(ListWidgetConfig.class, \"themevalue: \" + s);\n\t\t\t}\n\t\t}\n\n\t\tmBinding.widgetConfWrapper.sortingSpinner\n\t\t\t\t.setOnItemSelectedListener(new OnItemSelectedListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onItemSelected(AdapterView<?> parent, View view,\n\t\t\t\t\t\t\t\t\t\t\t   int pos, long id) {\n\t\t\t\t\t\twidgetPrefs.putString(KEY_SORT_TYPE, sortTypeValues[pos]);\n\t\t\t\t\t\t// Need to recreate loader for this\n\t\t\t\t\t\treloadTasks();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onNothingSelected(AdapterView<?> parent) {}\n\t\t\t\t});\n\t\tmBinding.widgetConfWrapper.sortingSpinner.setSelection(getArrayPositionOf(\n\t\t\t\tsortTypeValues,\n\t\t\t\twidgetPrefs.getString(KEY_SORT_TYPE, getString(R.string.default_sorttype))));\n\n\t\tmBinding.widgetConfWrapper.themeSpinner\n\t\t\t\t.setOnItemSelectedListener(new OnItemSelectedListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onItemSelected(AdapterView<?> parent, View view,\n\t\t\t\t\t\t\t\t\t\t\t   int pos, long id) {\n\t\t\t\t\t\tfinal String theme = parent.getItemAtPosition(pos).toString();\n\t\t\t\t\t\tfinal int mTheme;\n\t\t\t\t\t\tfinal int primaryTextColor;\n\t\t\t\t\t\tfinal int secondaryTextColor;\n\t\t\t\t\t\tif (theme.equals(getString(R.string.settings_summary_theme_light))) {\n\t\t\t\t\t\t\tmTheme = THEME_LIGHT;\n\t\t\t\t\t\t\tprimaryTextColor = ContextCompat.getColor(ListWidgetConfig.this,\n\t\t\t\t\t\t\t\t\tandroid.R.color.primary_text_light);\n\t\t\t\t\t\t\tsecondaryTextColor = ContextCompat.getColor(ListWidgetConfig.this,\n\t\t\t\t\t\t\t\t\tandroid.R.color.secondary_text_light);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmTheme = THEME_DARK;\n\t\t\t\t\t\t\tprimaryTextColor = ContextCompat.getColor(ListWidgetConfig.this,\n\t\t\t\t\t\t\t\t\tandroid.R.color.primary_text_dark);\n\t\t\t\t\t\t\tsecondaryTextColor = ContextCompat.getColor(ListWidgetConfig.this,\n\t\t\t\t\t\t\t\t\tandroid.R.color.secondary_text_dark);\n\t\t\t\t\t\t}\n\t\t\t\t\t\twidgetPrefs.putInt(KEY_THEME, mTheme);\n\t\t\t\t\t\twidgetPrefs.putInt(KEY_TEXTPRIMARY, primaryTextColor);\n\t\t\t\t\t\twidgetPrefs.putInt(KEY_TEXTSECONDARY, secondaryTextColor);\n\t\t\t\t\t\tupdateTheme(mTheme, widgetPrefs);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onNothingSelected(AdapterView<?> arg0) {}\n\t\t\t\t});\n\n\t\tfinal String currentThemeString;\n\t\tif (widgetPrefs.getInt(KEY_THEME, DEFAULT_THEME) == THEME_LIGHT) {\n\t\t\tcurrentThemeString = getString(R.string.settings_summary_theme_light);\n\t\t} else {\n\t\t\tcurrentThemeString = getString(R.string.settings_summary_theme_dark);\n\t\t}\n\t\tmBinding.widgetConfWrapper.themeSpinner.setSelection(getSpinnerPositionOf(\n\t\t\t\tmBinding.widgetConfWrapper.themeSpinner.getAdapter(), currentThemeString));\n\n\t\tmBinding.widgetConfWrapper.itemRowsSeekBar\n\t\t\t\t.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onStopTrackingTouch(SeekBar seekBar) {}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onStartTrackingTouch(SeekBar seekBar) {}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n\t\t\t\t\t\t// Plus one since seekbars start at zero\n\t\t\t\t\t\twidgetPrefs.putInt(KEY_TITLEROWS, progress + 1);\n\t\t\t\t\t\t// Only need to reload existing loader\n\t\t\t\t\t\tif (mNotesAdapter != null) {\n\t\t\t\t\t\t\tmNotesAdapter.notifyDataSetChanged();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tmBinding.widgetConfWrapper.itemRowsSeekBar\n\t\t\t\t.setProgress(widgetPrefs.getInt(KEY_TITLEROWS, DEFAULT_ROWS) - 1);\n\n\t\tmBinding.widgetConfWrapper.transparencySeekBar\n\t\t\t\t.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onStopTrackingTouch(SeekBar seekBar) {}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onStartTrackingTouch(SeekBar seekBar) {}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n\t\t\t\t\t\tfinal int color = getHomescreenBackgroundColor(progress,\n\t\t\t\t\t\t\t\twidgetPrefs.getInt(KEY_SHADE_COLOR, DEFAULT_SHADE));\n\n\t\t\t\t\t\twidgetPrefs.putInt(KEY_SHADE_COLOR, color);\n\t\t\t\t\t\tupdateBG(color);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t// Set current item\n\t\tint opacity = widgetPrefs.getInt(KEY_SHADE_COLOR, DEFAULT_SHADE);\n\t\t// Isolate the alpha\n\t\topacity = opacity >> 24;\n\t\topacity &= 0xff;\n\t\t// Get percentage\n\t\topacity = (100 * opacity) / 0xff;\n\t\tmBinding.widgetConfWrapper.transparencySeekBar.setProgress(opacity);\n\n\t\tmBinding.widgetConfWrapper.listSpinner\n\t\t\t\t.setOnItemSelectedListener(new OnItemSelectedListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onItemSelected(AdapterView<?> adapter, View arg1, int pos, long id) {\n\t\t\t\t\t\twidgetPrefs.putLong(KEY_LIST, id);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\twidgetPrefs.putString(KEY_LIST_TITLE, ((Cursor) adapter\n\t\t\t\t\t\t\t\t\t.getItemAtPosition(pos)).getString(1));\n\t\t\t\t\t\t} catch (ClassCastException e) {\n\t\t\t\t\t\t\t// Its the all lists item\n\t\t\t\t\t\t\twidgetPrefs.putString(KEY_LIST_TITLE,\n\t\t\t\t\t\t\t\t\t((String) adapter.getItemAtPosition(pos)));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Need to reload tasks\n\t\t\t\t\t\treloadTasks();\n\t\t\t\t\t\t// And set title\n\t\t\t\t\t\tmBinding.widgetPreviewWrapper.widgetPreview.titleButton\n\t\t\t\t\t\t\t\t.setText(widgetPrefs.getString(KEY_LIST_TITLE, \"\"));\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onNothingSelected(AdapterView<?> arg0) {}\n\t\t\t\t});\n\n\t\tmListAdapter = new ExtrasCursorAdapter(this,\n\t\t\t\tandroid.R.layout.simple_spinner_dropdown_item, null,\n\t\t\t\tnew String[] { TaskList.Columns.TITLE },\n\t\t\t\tnew int[] { android.R.id.text1 }, new int[] { ALL_LISTS_ID },\n\t\t\t\tnew int[] { R.string.show_from_all_lists },\n\t\t\t\tandroid.R.layout.simple_spinner_dropdown_item);\n\n\t\tmBinding.widgetConfWrapper.listSpinner.setAdapter(mListAdapter);\n\n\t\t// for each checkbox, set listener & initialize value\n\t\tmBinding.widgetConfWrapper.transparentHeaderCheckBox\n\t\t\t\t.setOnCheckedChangeListener((buttonView, isChecked) -> {\n\t\t\t\t\tmBinding.widgetPreviewWrapper.widgetPreview.widgetHeader\n\t\t\t\t\t\t\t.setVisibility(isChecked ? View.GONE : View.VISIBLE);\n\t\t\t\t\twidgetPrefs.putBoolean(KEY_HIDDENHEADER, isChecked);\n\t\t\t\t});\n\t\tmBinding.widgetConfWrapper.transparentHeaderCheckBox\n\t\t\t\t.setChecked(widgetPrefs.getBoolean(KEY_HIDDENHEADER, false));\n\n\t\tmBinding.widgetConfWrapper.showCompletedCheckBox\n\t\t\t\t.setOnCheckedChangeListener((btn, isChecked) -> {\n\t\t\t\t\twidgetPrefs.putBoolean(KEY_SHOWCOMPLETED, isChecked);\n\t\t\t\t\treloadTasks();\n\t\t\t\t});\n\t\tmBinding.widgetConfWrapper.showCompletedCheckBox\n\t\t\t\t.setChecked(widgetPrefs.getBoolean(KEY_SHOWCOMPLETED, false));\n\n\t\tmBinding.widgetConfWrapper.hideCheckBox\n\t\t\t\t.setOnCheckedChangeListener((buttonView, isChecked) -> {\n\t\t\t\t\twidgetPrefs.putBoolean(KEY_HIDDENCHECKBOX, isChecked);\n\t\t\t\t\tif (mNotesAdapter != null) mNotesAdapter.notifyDataSetChanged();\n\t\t\t\t});\n\t\tmBinding.widgetConfWrapper.hideCheckBox\n\t\t\t\t.setChecked(widgetPrefs.getBoolean(KEY_HIDDENCHECKBOX, false));\n\n\t\tmBinding.widgetConfWrapper.hideDateCheckBox\n\t\t\t\t.setOnCheckedChangeListener((buttonView, isChecked) -> {\n\t\t\t\t\twidgetPrefs.putBoolean(KEY_HIDDENDATE, isChecked);\n\t\t\t\t\tif (mNotesAdapter != null) mNotesAdapter.notifyDataSetChanged();\n\t\t\t\t});\n\t\tmBinding.widgetConfWrapper.hideDateCheckBox\n\t\t\t\t.setChecked(widgetPrefs.getBoolean(KEY_HIDDENDATE, false));\n\t}\n\n\tprivate static int getListPositionOf(final Adapter adapter, final long id) {\n\t\tif (adapter == null || adapter.getCount() == 0) return 0;\n\t\tint pos = 0;\n\t\tfor (int i = 0; i < adapter.getCount(); i++) {\n\t\t\tif (adapter.getItemId(i) == id) {\n\t\t\t\tpos = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn pos;\n\t}\n\n\tprivate static int getSpinnerPositionOf(final Adapter adapter, final String entry) {\n\t\tif (adapter == null || adapter.getCount() == 0) return 0;\n\t\tint pos = 0;\n\t\tfor (int i = 0; i < adapter.getCount(); i++) {\n\t\t\tif (adapter.getItem(i).toString().equals(entry)) {\n\t\t\t\tpos = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn pos;\n\t}\n\n\tprivate static int getArrayPositionOf(final String[] array, final String entry) {\n\t\tif (array == null || array.length == 0) return 0;\n\t\tint pos = 0;\n\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\tif (array[i].equals(entry)) {\n\t\t\t\tpos = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn pos;\n\t}\n\n\tvoid updateBG(final int color) {\n\t\tmBinding.widgetPreviewWrapper.widgetPreview.shade.setBackgroundColor(color);\n\t\tmBinding.widgetPreviewWrapper.widgetPreview.shade\n\t\t\t\t.setVisibility((color & 0xff000000) == 0 ? View.GONE : View.VISIBLE);\n\t}\n\n\tvoid updateTheme(final int theme, final WidgetPrefs widgetPrefs) {\n\t\tint color;\n\t\tint alpha = widgetPrefs.getInt(KEY_SHADE_COLOR, DEFAULT_SHADE);\n\t\t// Isolate alpha channel\n\t\talpha = 0xff000000 & alpha;\n\t\tswitch (theme) {\n\t\t\tcase THEME_LIGHT:\n\t\t\t\t// WHITE\n\t\t\t\tcolor = 0xffffff;\n\t\t\t\tbreak;\n\t\t\tcase THEME_DARK:\n\t\t\tdefault:\n\t\t\t\tcolor = 0;\n\t\t\t\tbreak;\n\t\t}\n\t\t// Add alpha\n\t\tcolor = alpha | color;\n\t\twidgetPrefs.putInt(KEY_SHADE_COLOR, color);\n\t\tupdateBG(color);\n\t\tmNotesAdapter.notifyDataSetChanged();\n\t}\n\n\t/**\n\t * Returns black, with the opacity specified\n\t *\n\t * @param opacity should be a number between 0 and 100\n\t */\n\tpublic static int getHomescreenBackgroundColor(final int opacity) {\n\t\tif (opacity >= 100) {\n\t\t\treturn 0xff000000;\n\t\t} else if (opacity <= 0) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\treturn (opacity * 256 / 100) << 24;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the specified color, with the opacity specified. The color will\n\t * have its alpha overwritten.\n\t */\n\tpublic static int getHomescreenBackgroundColor(final int opacity, final int color) {\n\t\t// Get rid of possible alpha\n\t\tint retColor = color & 0x00ffffff;\n\n\t\treturn getHomescreenBackgroundColor(opacity) | retColor;\n\t}\n\n\tstatic class SimpleWidgetPreviewAdapter extends SimpleCursorAdapter {\n\t\tfinal int mItemLayout;\n\t\tfinal int mHeaderLayout;\n\t\tfinal static int itemType = 0;\n\t\tfinal static int headerType = 1;\n\t\tfinal Context mContext;\n\n\t\tpublic SimpleWidgetPreviewAdapter(Context context, int layout, int headerLayout,\n\t\t\t\t\t\t\t\t\t\t  Cursor c, String[] from, int[] to, int flags) {\n\t\t\tsuper(context, layout, c, from, to, flags);\n\t\t\tmItemLayout = layout;\n\t\t\tmHeaderLayout = headerLayout;\n\t\t\tmContext = context;\n\t\t}\n\n\t\tint getViewLayout(final int position) {\n\t\t\tif (itemType == getItemViewType(position)) {\n\t\t\t\treturn mItemLayout;\n\t\t\t} else {\n\t\t\t\treturn mHeaderLayout;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic int getViewTypeCount() {\n\t\t\treturn 2;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getItemViewType(int position) {\n\t\t\tfinal Cursor c = (Cursor) getItem(position);\n\t\t\t// If the id is invalid, it's a header\n\t\t\tif (c.getLong(0) < 1) {\n\t\t\t\treturn headerType;\n\t\t\t} else {\n\t\t\t\treturn itemType;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\t\tif (convertView == null) {\n\t\t\t\tfinal LayoutInflater inflater = LayoutInflater.from(this.mContext);\n\t\t\t\tconvertView = inflater.inflate(getViewLayout(position), parent, false);\n\t\t\t}\n\t\t\treturn super.getView(position, convertView, parent);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetProvider.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.widget.list;\n\nimport android.annotation.SuppressLint;\nimport android.app.PendingIntent;\nimport android.appwidget.AppWidgetManager;\nimport android.appwidget.AppWidgetProvider;\nimport android.appwidget.AppWidgetProviderInfo;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.view.View;\nimport android.widget.RemoteViews;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\nimport com.nononsenseapps.notepad.NotePadBroadcastReceiver;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.fragments.TaskDetailFragment;\n\nimport java.util.Objects;\n\n/**\n * The note-list widget's AppWidgetProvider.\n */\npublic class ListWidgetProvider extends AppWidgetProvider {\n\n\t// private static final String TAG = \"WIDGETPROVIDER\";\n\tpublic static final String COMPLETE_ACTION = \"com.nononsenseapps.notepad.widget.COMPLETE\";\n\tpublic static final String CLICK_ACTION = \"com.nononsenseapps.notepad.widget.CLICK\";\n\tpublic static final String OPEN_ACTION = \"com.nononsenseapps.notepad.widget.OPENAPP\";\n\tpublic static final String CONFIG_ACTION = \"com.nononsenseapps.notepad.widget.CONFIG\";\n\tpublic static final String CREATE_ACTION = \"com.nononsenseapps.notepad.widget.CREATE\";\n\tpublic static final String EXTRA_NOTE_ID = \"com.nononsenseapps.notepad.widget.note_id\";\n\tpublic static final String EXTRA_LIST_ID = \"com.nononsenseapps.notepad.widget.list_id\";\n\n\t// called by android.app.AppComponentFactory\n\tpublic ListWidgetProvider() {}\n\n\t/**\n\t * When the user touches the checkbox on a list item in the widget, an intent is launched.\n\t * This receives that signal. In other words, Complete note calls go here\n\t */\n\t@Override\n\tpublic void onReceive(Context context, Intent intent) {\n\t\tString action = intent.getAction();\n\t\tObjects.requireNonNull(action);\n\n\t\tif (action.equals(CLICK_ACTION)) {\n\t\t\tNnnLogger.debug(ListWidgetProvider.class, \"CLICK ACTION RECEIVED\");\n\t\t\tlong noteId = intent.getLongExtra(EXTRA_NOTE_ID, -1);\n\t\t\tif (noteId > -1) {\n\t\t\t\tIntent appIntent = new Intent()\n\t\t\t\t\t\t.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)\n\t\t\t\t\t\t.setAction(Intent.ACTION_EDIT)\n\t\t\t\t\t\t.setData(Task.getUri(noteId))\n\t\t\t\t\t\t.putExtra(TaskDetailFragment.ARG_ITEM_LIST_ID,\n\t\t\t\t\t\t\t\tintent.getLongExtra(EXTRA_LIST_ID, -1));\n\n\t\t\t\tcontext.startActivity(appIntent);\n\t\t\t}\n\t\t} else if (action.equals(COMPLETE_ACTION)) {\n\t\t\t// Should send broadcast here\n\t\t\tNnnLogger.debug(ListWidgetProvider.class, \"COMPLETE ACTION RECEIVED\");\n\t\t\tlong noteId = intent.getLongExtra(EXTRA_NOTE_ID, -1);\n\t\t\t// This will complete the note\n\t\t\tif (noteId > -1) {\n\t\t\t\t// choose if you have to set the note as complete or incomplete\n\t\t\t\tString action2 = Task.byId(noteId, context).isCompleted()\n\t\t\t\t\t\t? NotePadBroadcastReceiver.SET_NOTE_INCOMPLETE\n\t\t\t\t\t\t: NotePadBroadcastReceiver.SET_NOTE_COMPLETE;\n\n\t\t\t\tIntent bintent = new Intent(context, NotePadBroadcastReceiver.class)\n\t\t\t\t\t\t.setAction(action2)\n\t\t\t\t\t\t.putExtra(Task.Columns._ID, noteId);\n\t\t\t\tcontext.sendBroadcast(bintent);\n\t\t\t}\n\t\t}\n\n\t\tsuper.onReceive(context, intent);\n\t}\n\n\t@Override\n\tpublic void onEnabled(Context context) {\n\t\t/*\n\t\t * Register for external updates to the data to trigger an update of the\n\t\t * widget. When using content providers, the data is often updated via a\n\t\t * background service, or in response to user interaction in the main\n\t\t * app. To ensure that the widget always reflects the current state of\n\t\t * the data, we must listen for changes and update ourselves\n\t\t * accordingly.\n\t\t */\n\t}\n\n\t@Override\n\tpublic void onDeleted(Context context, int[] appWidgetIds) {\n\t\tsuper.onDeleted(context, appWidgetIds);\n\t\tNnnLogger.debug(ListWidgetProvider.class,\n\t\t\t\t\"onDeleted, appWidgetIds.length = \" + appWidgetIds.length);\n\n\t\tfor (int widgetId : appWidgetIds) {\n\t\t\tWidgetPrefs.delete(context, widgetId);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {\n\t\t// This is not called on start up if we are using a configuration activity\n\n\t\t// Update each of the widgets with the remote adapter\n\t\tfor (int widgetId : appWidgetIds) {\n\t\t\t// Load widget prefs\n\t\t\tfinal WidgetPrefs prefs = new WidgetPrefs(context, widgetId);\n\n\t\t\t// Build view update\n\t\t\tRemoteViews updateViews = buildRemoteViews(context, appWidgetManager, widgetId, prefs);\n\n\t\t\t// Tell the AppWidgetManager to perform an update\n\t\t\tappWidgetManager.updateAppWidget(widgetId, updateViews);\n\t\t}\n\t}\n\n\tpublic static RemoteViews buildRemoteViews(final Context context,\n\t\t\t\t\t\t\t\t\t\t\t   final AppWidgetManager appWidgetManager,\n\t\t\t\t\t\t\t\t\t\t\t   final int appWidgetId,\n\t\t\t\t\t\t\t\t\t\t\t   final WidgetPrefs settings) {\n\t\t// Hack: We must set this widget's id in the URI to prevent the situation\n\t\t// where the last widget added will be used for everything\n\t\tfinal Uri data = Uri.withAppendedPath(Uri.parse(\"STUPIDWIDGETS\"\n\t\t\t\t+ \"://widget/id/\"), String.valueOf(appWidgetId));\n\n\t\t// Get the value of OPTION_APPWIDGET_HOST_CATEGORY\n\t\tint category = appWidgetManager\n\t\t\t\t.getAppWidgetOptions(appWidgetId)\n\t\t\t\t.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);\n\n\t\t// If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen widget\n\t\tboolean isKeyguard = category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;\n\n\t\tif (isKeyguard) {\n\t\t\tsettings.putBoolean(ListWidgetConfig.KEY_LOCKSCREEN, true);\n\t\t}\n\n\t\t// Specify the service to provide data for the collection widget. Note that we need to\n\t\t// embed the appWidgetId via the data otherwise it will be ignored.\n\t\tfinal Intent intent = new Intent(context, ListWidgetService.class);\n\t\tintent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);\n\t\tintent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));\n\n\t\tRemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);\n\t\trv.setRemoteAdapter(R.id.notesList, intent);\n\n\t\t// Set the empty view to be displayed if the collection is empty. It must be a sibling\n\t\t// view of the collection view.\n\t\trv.setEmptyView(R.id.notesList, R.id.empty_view);\n\n\t\tfinal long listId = settings.getLong(ListWidgetConfig.KEY_LIST, -1);\n\t\tfinal String listTitle = settings.getString(ListWidgetConfig.KEY_LIST_TITLE,\n\t\t\t\tcontext.getString(R.string.app_name_short));\n\t\trv.setTextViewText(R.id.titleButton, listTitle);\n\n\t\t// Hide header if we should\n\t\trv.setViewVisibility(R.id.widgetHeader,\n\t\t\t\tsettings.getBoolean(ListWidgetConfig.KEY_HIDDENHEADER, false)\n\t\t\t\t\t\t? View.GONE : View.VISIBLE);\n\n\t\t// Set background color\n\t\tfinal int color = settings\n\t\t\t\t.getInt(ListWidgetConfig.KEY_SHADE_COLOR, ListWidgetConfig.DEFAULT_SHADE);\n\t\trv.setInt(R.id.shade, \"setBackgroundColor\", color);\n\t\trv.setViewVisibility(R.id.shade, (color & 0xff000000) == 0 ? View.GONE : View.VISIBLE);\n\n\t\t/*\n\t\t * Bind a click listener template for the contents of the list. Note\n\t\t * that we need to update the intent's data if we set an extra, since\n\t\t * the extras will be ignored otherwise.\n\t\t */\n\t\tif (isKeyguard) {\n\t\t\tfinal Intent itemIntent = new Intent(context, ActivityMain_.class);\n\t\t\titemIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);\n\n\t\t\tPendingIntent onClickPendingIntent = getThePendingIntentForActivity(itemIntent, context);\n\t\t\trv.setPendingIntentTemplate(R.id.notesList, onClickPendingIntent);\n\t\t} else {\n\t\t\t// To handle complete, we use broadcasts\n\t\t\tIntent onClickIntent = new Intent(context, ListWidgetProvider.class);\n\t\t\tonClickIntent\n\t\t\t\t\t.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)\n\t\t\t\t\t.putExtra(Task.Columns.DBLIST, listId)\n\t\t\t\t\t.setData(data);\n\n\t\t\tPendingIntent onClickPendingIntent =\n\t\t\t\t\tgetThePendingIntentForBroadcast(onClickIntent, context);\n\t\t\trv.setPendingIntentTemplate(R.id.notesList, onClickPendingIntent);\n\t\t}\n\n\t\tfinal Intent appIntent = new Intent();\n\t\tappIntent\n\t\t\t\t.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)\n\t\t\t\t.setClass(context, ActivityMain_.class)\n\t\t\t\t.setAction(Intent.ACTION_VIEW)\n\t\t\t\t.setData(TaskList.getUri(listId));\n\t\tPendingIntent openAppPendingIntent = getThePendingIntentForActivity(appIntent, context);\n\t\trv.setOnClickPendingIntent(R.id.titleButton, openAppPendingIntent);\n\n\t\tfinal Intent configIntent = new Intent();\n\t\tconfigIntent\n\t\t\t\t.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)\n\t\t\t\t.setClass(context, ListWidgetConfig.class)\n\t\t\t\t.setData(data)\n\t\t\t\t.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);\n\t\tPendingIntent openConfigPendingIntent = getThePendingIntentForActivity(configIntent, context);\n\t\trv.setOnClickPendingIntent(R.id.widgetConfigButton, openConfigPendingIntent);\n\n\t\t// + button to create a new note from the widget\n\t\tfinal Intent createIntent = new Intent();\n\t\tcreateIntent\n\t\t\t\t.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)\n\t\t\t\t.setClass(context, ActivityMain_.class)\n\t\t\t\t// Append a dummy path so we don't override this intent on 2nd, 3rd, etc, widgets.\n\t\t\t\t.setAction(Intent.ACTION_INSERT)\n\t\t\t\t.setData(Uri.withAppendedPath(Task.URI, \"/widget/\" + appWidgetId + \"/-1\"))\n\t\t\t\t.putExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, listId);\n\n\t\tif (listId > 0) {\n\t\t\trv.setViewVisibility(R.id.createNoteButton, View.VISIBLE);\n\t\t\tPendingIntent createPendingIntent = getThePendingIntentForActivity(createIntent, context);\n\t\t\trv.setOnClickPendingIntent(R.id.createNoteButton, createPendingIntent);\n\t\t} else {\n\t\t\t// the widget is showing notes from all lists: hide the + button,\n\t\t\t// because it would not find a valid list to add a note to\n\t\t\trv.setViewVisibility(R.id.createNoteButton, View.GONE);\n\t\t}\n\n\t\treturn rv;\n\t}\n\n\t/**\n\t * This uses getActivity(), not getBroadcast() !\n\t *\n\t * @return a properly configured {@link PendingIntent} for the given {@link Intent}\n\t */\n\t@SuppressLint(\"UnspecifiedImmutableFlag\")\n\tprivate static PendingIntent getThePendingIntentForActivity(Intent i, Context c) {\n\t\tPendingIntent pi;\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n\t\t\t// on API 31 and higher the mutability must be explicitly set\n\t\t\tpi = PendingIntent.getActivity(c, 0, i,\n\t\t\t\t\tPendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);\n\t\t} else {\n\t\t\t// on lower API levels, the mutability is implied\n\t\t\tpi = PendingIntent.getActivity(c, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);\n\t\t}\n\t\treturn pi;\n\t}\n\n\t/**\n\t * Like {@link ListWidgetProvider#getThePendingIntentForActivity(Intent, Context)}\n\t * but this one uses getBroadcast(), not getActivity() !\n\t */\n\t@SuppressLint(\"UnspecifiedImmutableFlag\")\n\tprivate static PendingIntent getThePendingIntentForBroadcast(Intent i, Context c) {\n\t\tPendingIntent pi;\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n\t\t\tpi = PendingIntent.getBroadcast(c, 0, i,\n\t\t\t\t\tPendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);\n\t\t} else {\n\t\t\tpi = PendingIntent.getBroadcast(c, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);\n\t\t}\n\t\treturn pi;\n\t}\n\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetService.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.widget.list;\n\nimport android.appwidget.AppWidgetManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Binder;\nimport android.view.View;\nimport android.widget.RemoteViews;\nimport android.widget.RemoteViewsService;\n\nimport com.nononsenseapps.helpers.ThemeHelper;\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.fragments.TaskDetailFragment;\nimport com.nononsenseapps.ui.TitleNoteTextView;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n/**\n * This is the service that provides the factory to be bound to the collection service\n */\npublic class ListWidgetService extends RemoteViewsService {\n\n\t@Override\n\tpublic RemoteViewsFactory onGetViewFactory(Intent intent) {\n\t\treturn new ListRemoteViewsFactory(this.getApplicationContext(), intent);\n\t}\n\n\t/**\n\t * This is the factory that will provide data to the collection widget\n\t */\n\tstatic class ListRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {\n\n\t\t/**\n\t\t * column names of this cursor are in {@link Task.Columns#FIELDS}\n\t\t */\n\t\tprivate Cursor mCursor;\n\n\t\tfinal private Context mContext;\n\t\tfinal private int mAppWidgetId;\n\n\t\t// these 2 must be reloaded every time, to react to changes in locale preferences\n\t\tprivate SimpleDateFormat mDateFormatter = null;\n\n\t\tpublic ListRemoteViewsFactory(Context context, Intent intent) {\n\t\t\tmContext = context;\n\t\t\tmAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,\n\t\t\t\t\tAppWidgetManager.INVALID_APPWIDGET_ID);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCreate() {}\n\n\t\t@Override\n\t\tpublic void onDestroy() {\n\t\t\tif (mCursor == null) return;\n\t\t\tmCursor.close();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getCount() {\n\t\t\tif (mCursor == null) return 0;\n\t\t\treturn mCursor.getCount();\n\t\t}\n\n\t\t@Override\n\t\tpublic RemoteViews getViewAt(int position) {\n\t\t\t// Get widget settings\n\t\t\tfinal WidgetPrefs widgetPrefs = new WidgetPrefs(mContext, mAppWidgetId);\n\t\t\tif (!widgetPrefs.isPresent()) {\n\t\t\t\t// basically \"return null\", but that started crashing reccently,\n\t\t\t\t// so we return an empty meaningless view\n\t\t\t\treturn new RemoteViews(mContext.getPackageName(), R.layout.widgetlist_header);\n\t\t\t}\n\n\t\t\t// load date formatter if not present\n\t\t\tif (mDateFormatter == null) {\n\t\t\t\tmDateFormatter = TimeFormatter.getLocalFormatterMicro(mContext);\n\t\t\t}\n\n\t\t\tfinal long listId = widgetPrefs\n\t\t\t\t\t.getLong(ListWidgetConfig.KEY_LIST, ListWidgetConfig.ALL_LISTS_ID);\n\t\t\tfinal int theme = widgetPrefs\n\t\t\t\t\t.getInt(ListWidgetConfig.KEY_THEME, ListWidgetConfig.DEFAULT_THEME);\n\t\t\tfinal int primaryTextColor = widgetPrefs\n\t\t\t\t\t.getInt(ListWidgetConfig.KEY_TEXTPRIMARY, ListWidgetConfig.DEFAULT_TEXTPRIMARY);\n\t\t\tfinal int rows = widgetPrefs\n\t\t\t\t\t.getInt(ListWidgetConfig.KEY_TITLEROWS, ListWidgetConfig.DEFAULT_ROWS);\n\t\t\tfinal boolean isCheckboxHidden = widgetPrefs\n\t\t\t\t\t.getBoolean(ListWidgetConfig.KEY_HIDDENCHECKBOX, false);\n\t\t\tboolean isDateHidden = widgetPrefs\n\t\t\t\t\t.getBoolean(ListWidgetConfig.KEY_HIDDENDATE, false);\n\n\t\t\t// TODO rest\n\n\t\t\tRemoteViews rv = null;\n\t\t\tif (mCursor.moveToPosition(position)) {\n\n\t\t\t\tboolean isHeader = mCursor.getLong(0) < 1;\n\t\t\t\tif (isHeader) {\n\t\t\t\t\trv = new RemoteViews(mContext.getPackageName(), R.layout.widgetlist_header);\n\t\t\t\t\trv.setTextColor(android.R.id.text1, primaryTextColor);\n\n\t\t\t\t\tString sTemp = mCursor.getString(1);\n\t\t\t\t\tlong dueDateMillis = mCursor.getLong(4);\n\t\t\t\t\tsTemp = Task.getHeaderNameForListSortedByDate(sTemp, dueDateMillis, mContext);\n\n\t\t\t\t\t// Set text\n\t\t\t\t\trv.setTextViewText(android.R.id.text1, sTemp);\n\t\t\t\t\t// if you don't see the update, but a \"Loading...\" message instead, you may\n\t\t\t\t\t// have made a mistake (in the layout xml file) that the widget doesn't forgive\n\t\t\t\t} else {\n\t\t\t\t\trv = new RemoteViews(mContext.getPackageName(), R.layout.widgetlist_item);\n\n\t\t\t\t\t// \"Complete\" checkbox. RemoteViews limitations:\n\t\t\t\t\t// * this ImageButton simulates a checkbox for android widgets\n\t\t\t\t\t// * you can't use the actual CheckBox in API < 31\n\t\t\t\t\t// * Used in widgetlist_item.xml\n\t\t\t\t\t// * we can't call setChecked() on ImageButtons, so we change the drawable\n\t\t\t\t\t// * we also can't use setSelected, setActivated, setChecked on the widget\n\t\t\t\t\tfinal int visibleCheckBox;\n\t\t\t\t\tfinal int hiddenCheckBox;\n\t\t\t\t\tif (theme == ListWidgetConfig.THEME_LIGHT) {\n\t\t\t\t\t\t// show only the \"light\" imagebutton for the light theme\n\t\t\t\t\t\thiddenCheckBox = R.id.completedCheckBoxDark;\n\t\t\t\t\t\tvisibleCheckBox = R.id.completedCheckBoxLight;\n\t\t\t\t\t} else {\n\t\t\t\t\t\thiddenCheckBox = R.id.completedCheckBoxLight;\n\t\t\t\t\t\tvisibleCheckBox = R.id.completedCheckBoxDark;\n\t\t\t\t\t}\n\n\t\t\t\t\t// 0 if user did not complete the task, > 0 otherwise\n\t\t\t\t\tlong millisOfCompletion = mCursor.getLong(3);\n\t\t\t\t\tif (millisOfCompletion > 0) {\n\t\t\t\t\t\trv.setImageViewResource(visibleCheckBox, R.drawable.ic_checkbox_checked);\n\t\t\t\t\t} else {\n\t\t\t\t\t\trv.setImageViewResource(visibleCheckBox, R.drawable.ic_checkbox_unchecked);\n\t\t\t\t\t}\n\t\t\t\t\t// use the accent color to tint the checkboxes.\n\t\t\t\t\t// You could also use primaryTextColor, if users complain ...\n\t\t\t\t\tint checkboxColor = ThemeHelper.getThemeAccentColor(mContext);\n\t\t\t\t\trv.setInt(visibleCheckBox, \"setColorFilter\", checkboxColor);\n\n\t\t\t\t\trv.setViewVisibility(hiddenCheckBox, View.GONE);\n\t\t\t\t\trv.setViewVisibility(visibleCheckBox,\n\t\t\t\t\t\t\tisCheckboxHidden ? View.GONE : View.VISIBLE);\n\t\t\t\t\t// Spacer\n\t\t\t\t\trv.setViewVisibility(R.id.itemSpacer,\n\t\t\t\t\t\t\tisCheckboxHidden ? View.GONE : View.VISIBLE);\n\n\t\t\t\t\t// Date\n\t\t\t\t\tif (mCursor.isNull(4)) {\n\t\t\t\t\t\trv.setTextViewText(R.id.dueDate, \"\");\n\t\t\t\t\t\tisDateHidden = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\trv.setTextViewText(R.id.dueDate, mDateFormatter\n\t\t\t\t\t\t\t\t.format(new Date(mCursor.getLong(4))));\n\t\t\t\t\t}\n\t\t\t\t\trv.setViewVisibility(R.id.dueDate, isDateHidden ? View.GONE : View.VISIBLE);\n\t\t\t\t\trv.setTextColor(R.id.dueDate, primaryTextColor);\n\n\t\t\t\t\t// Text\n\t\t\t\t\trv.setTextColor(android.R.id.text1, primaryTextColor);\n\t\t\t\t\trv.setInt(android.R.id.text1, \"setMaxLines\", rows);\n\n\t\t\t\t\t// Only if task it not locked\n\t\t\t\t\tif (mCursor.getInt(9) != 1) {\n\t\t\t\t\t\trv.setTextViewText(android.R.id.text1, TitleNoteTextView.getStyledText(\n\t\t\t\t\t\t\t\tmCursor.getString(1), mCursor.getString(2),\n\t\t\t\t\t\t\t\t1.0f, 1, 0));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Just title\n\t\t\t\t\t\trv.setTextViewText(android.R.id.text1, TitleNoteTextView.getStyledText(\n\t\t\t\t\t\t\t\tmCursor.getString(1), 1.0f, 1, 0));\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the click intent\n\t\t\t\t\tif (widgetPrefs.getBoolean(ListWidgetConfig.KEY_LOCKSCREEN, false)) {\n\t\t\t\t\t\tfinal Intent clickIntent = new Intent()\n\t\t\t\t\t\t\t\t.setAction(Intent.ACTION_EDIT)\n\t\t\t\t\t\t\t\t.setData(Task.getUri(mCursor.getLong(0)))\n\t\t\t\t\t\t\t\t.putExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, listId);\n\t\t\t\t\t\trv.setOnClickFillInIntent(R.id.widget_item, clickIntent);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// on the launcher, not on the lock screen\n\t\t\t\t\t\tfinal Intent fillInIntent = new Intent()\n\t\t\t\t\t\t\t\t.setAction(ListWidgetProvider.CLICK_ACTION)\n\t\t\t\t\t\t\t\t.putExtra(ListWidgetProvider.EXTRA_NOTE_ID, mCursor.getLong(0))\n\t\t\t\t\t\t\t\t.putExtra(ListWidgetProvider.EXTRA_LIST_ID, listId);\n\t\t\t\t\t\trv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set complete broadcast\n\t\t\t\t\tfinal Intent completeIntent = new Intent();\n\t\t\t\t\tif (widgetPrefs.getBoolean(ListWidgetConfig.KEY_LOCKSCREEN, false)) {\n\t\t\t\t\t\t// on lock screen => have to open note\n\t\t\t\t\t\tcompleteIntent\n\t\t\t\t\t\t\t\t.setAction(Intent.ACTION_EDIT)\n\t\t\t\t\t\t\t\t.setData(Task.getUri(mCursor.getLong(0)))\n\t\t\t\t\t\t\t\t.putExtra(TaskDetailFragment.ARG_ITEM_LIST_ID, listId);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// the pseudo-checkbox of a note was pressed while on the launcher\n\t\t\t\t\t\t// => not on lock screen => send broadcast to complete.\n\t\t\t\t\t\tcompleteIntent\n\t\t\t\t\t\t\t\t.setAction(ListWidgetProvider.COMPLETE_ACTION)\n\t\t\t\t\t\t\t\t.putExtra(ListWidgetProvider.EXTRA_NOTE_ID,\n\t\t\t\t\t\t\t\t\t\tmCursor.getLong(0));\n\t\t\t\t\t}\n\t\t\t\t\trv.setOnClickFillInIntent(R.id.completedCheckBoxDark, completeIntent);\n\t\t\t\t\trv.setOnClickFillInIntent(R.id.completedCheckBoxLight, completeIntent);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn rv;\n\t\t}\n\n\t\t/**\n\t\t * We aren't going to return a custom loading view, so the OS will show some text like\n\t\t * \"Loading...\" or \"Caricamento...\" depending on the system locale.\n\t\t */\n\t\t@Override\n\t\tpublic RemoteViews getLoadingView() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getViewTypeCount() {\n\t\t\treturn 2;\n\t\t}\n\n\t\t@Override\n\t\tpublic long getItemId(int position) {\n\t\t\treturn position;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasStableIds() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onDataSetChanged() {\n\t\t\t// Revert back to our process' identity so we can work with our content provider\n\t\t\tfinal long identityToken = Binder.clearCallingIdentity();\n\n\t\t\t// Refresh the cursor\n\t\t\tif (mCursor != null) {\n\t\t\t\tmCursor.close();\n\t\t\t}\n\n\t\t\t// (re)load dateformatter in case preferences changed\n\t\t\tmDateFormatter = TimeFormatter.getLocalFormatterMicro(mContext);\n\n\t\t\t// Get widget settings\n\t\t\tfinal WidgetPrefs widgetPrefs = new WidgetPrefs(mContext, mAppWidgetId);\n\n\t\t\tfinal Uri targetUri;\n\t\t\tfinal long listId = widgetPrefs.getLong(ListWidgetConfig.KEY_LIST,\n\t\t\t\t\tListWidgetConfig.ALL_LISTS_ID);\n\t\t\tfinal String sortSpec;\n\t\t\tfinal String sortType = widgetPrefs.getString(ListWidgetConfig.KEY_SORT_TYPE,\n\t\t\t\t\tmContext.getString(R.string.default_sorttype));\n\t\t\tboolean isShowingCompleted = widgetPrefs\n\t\t\t\t\t.getBoolean(ListWidgetConfig.KEY_SHOWCOMPLETED, false);\n\n\t\t\tif (sortType.equals(mContext.getString(R.string.const_possubsort)) && listId > 0) {\n\t\t\t\ttargetUri = Task.URI;\n\t\t\t\tsortSpec = Task.Columns.LEFT;\n\t\t\t} else if (sortType.equals(mContext.getString(R.string.const_modified))) {\n\t\t\t\ttargetUri = Task.URI;\n\t\t\t\tsortSpec = Task.Columns.UPDATED + \" DESC\";\n\t\t\t} else if (sortType.equals(mContext.getString(R.string.const_duedate))) {\n\t\t\t\t// due date sorting\n\t\t\t\ttargetUri = Task.URI_SECTIONED_BY_DATE;\n\t\t\t\tsortSpec = null;\n\t\t\t} else {\n\t\t\t\t// Alphabetic\n\t\t\t\ttargetUri = Task.URI;\n\t\t\t\tsortSpec = mContext.getString(R.string.const_as_alphabetic, Task.Columns.TITLE);\n\t\t\t}\n\n\t\t\tString listWhere;\n\t\t\tString[] listArg;\n\n\n\t\t\tif (listId > 0) {\n\t\t\t\t// only get notes in that list id\n\t\t\t\tlistArg = new String[] { Long.toString(listId) };\n\n\t\t\t\t// if user does not want to also show completed tasks in widget, the query\n\t\t\t\t// will filter away database records with a \"completed\" unix time\n\t\t\t\tlistWhere = isShowingCompleted\n\t\t\t\t\t\t? \"CAST(\" + Task.Columns.DBLIST + \" AS INTEGER) IS ?\"\n\t\t\t\t\t\t: \"CAST(\" + Task.Columns.DBLIST + \" AS INTEGER) IS ? AND \" + Task.Columns.COMPLETED + \" IS NULL\";\n\t\t\t} else {\n\t\t\t\t// all list ids\n\t\t\t\tlistArg = null;\n\n\t\t\t\t// if user wants to show completed tasks, since here it shows from all lists,\n\t\t\t\t// then this \"where\" should show everything. In android logic, that means\n\t\t\t\t// sending \"null\" to .query() here below\n\t\t\t\tlistWhere = isShowingCompleted\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: Task.Columns.COMPLETED + \" IS NULL\";\n\t\t\t}\n\n\t\t\t// TODO this is a very slow query, it takes 40 seconds. See #574. Of these, 20\n\t\t\t//  can be shaved off by removing CAST() from listWhere in this function, but\n\t\t\t//  that would cause notes to disappear from the list widget when sorting by\n\t\t\t//  due date and showing only one note list, see #560\n\t\t\tmCursor = mContext\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.query(targetUri, Task.Columns.FIELDS, listWhere, listArg, sortSpec);\n\n\t\t\t// Restore the identity - not sure if it's needed since we're going\n\t\t\t// to return right here, but it just *seems* cleaner\n\t\t\tBinder.restoreCallingIdentity(identityToken);\n\t\t}\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/WidgetPrefs.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.widget.list;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\n/**\n * An helper class to interact with shared preferences related to the \"list widget\"\n */\npublic class WidgetPrefs {\n\n\tpublic final static String PREFS_KEY = \"NotesListWidget\";\n\tpublic final static String WIDGET_PRESENT_KEY = \"WidgetPresent\";\n\tpublic final static boolean WIDGET_PRESENT_DEFAULT = false;\n\n\tprivate final int widgetId;\n\tprivate final SharedPreferences prefs;\n\tprivate SharedPreferences.Editor prefsEditor = null;\n\n\tpublic static void delete(final Context context, final int widgetId) {\n\t\tSharedPreferences prefs = context.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);\n\t\tif (prefs != null) {\n\t\t\tSharedPreferences.Editor edit = prefs.edit();\n\t\t\tif (edit != null) {\n\t\t\t\tedit.remove(keyWrap(WIDGET_PRESENT_KEY, widgetId)).commit();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic WidgetPrefs(final Context context, final int widgetId) {\n\t\tthis.widgetId = widgetId;\n\t\tprefs = context.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);\n\t}\n\n\tpublic String keyWrap(final String originalKey) {\n\t\treturn keyWrap(originalKey, widgetId);\n\t}\n\n\tpublic static String keyWrap(final String originalKey, final int widgetId) {\n\t\treturn originalKey + widgetId;\n\t}\n\n\tpublic boolean isPresent() {\n\t\tif (prefs != null) {\n\t\t\treturn prefs.getBoolean(keyWrap(WIDGET_PRESENT_KEY), WIDGET_PRESENT_DEFAULT);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void setPresent() {\n\t\tputBoolean(WIDGET_PRESENT_KEY, true);\n\t}\n\n\tpublic boolean putBoolean(String key, boolean value) {\n\t\tif (prefs != null && prefsEditor == null) {\n\t\t\tprefsEditor = prefs.edit();\n\t\t}\n\t\tif (prefsEditor != null) {\n\t\t\tprefsEditor.putBoolean(keyWrap(key), value).commit();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean getBoolean(String key, boolean defValue) {\n\t\tif (prefs != null) {\n\t\t\ttry {\n\t\t\t\treturn prefs.getBoolean(keyWrap(key), defValue);\n\t\t\t} catch (ClassCastException e) {\n\t\t\t\t// Return default value\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void putString(String key, String value) {\n\t\tif (prefs != null && prefsEditor == null) {\n\t\t\tprefsEditor = prefs.edit();\n\t\t}\n\t\tif (prefsEditor != null) {\n\t\t\tprefsEditor.putString(keyWrap(key), value).commit();\n\t\t}\n\t}\n\n\tpublic String getString(String key, String defValue) {\n\t\tif (prefs != null) {\n\t\t\ttry {\n\t\t\t\treturn prefs.getString(keyWrap(key), defValue);\n\t\t\t} catch (ClassCastException e) {\n\t\t\t\t// Return default value\n\t\t\t}\n\t\t}\n\t\treturn defValue;\n\t}\n\n\tpublic int getInt(String key, int defValue) {\n\t\tif (prefs != null) {\n\t\t\ttry {\n\t\t\t\treturn prefs.getInt(keyWrap(key), defValue);\n\t\t\t} catch (ClassCastException e) {\n\t\t\t\t// Return default value\n\t\t\t}\n\t\t}\n\t\treturn defValue;\n\t}\n\n\tpublic void putInt(String key, int value) {\n\t\tif (prefs != null && prefsEditor == null) {\n\t\t\tprefsEditor = prefs.edit();\n\t\t}\n\t\tif (prefsEditor != null) {\n\t\t\tprefsEditor.putInt(keyWrap(key), value).commit();\n\t\t}\n\t}\n\n\tpublic void putLong(String key, long value) {\n\t\tif (prefs != null && prefsEditor == null) {\n\t\t\tprefsEditor = prefs.edit();\n\t\t}\n\t\tif (prefsEditor != null) {\n\t\t\tprefsEditor.putLong(keyWrap(key), value).commit();\n\t\t}\n\t}\n\n\tpublic long getLong(String key, long defValue) {\n\t\tif (prefs != null) {\n\t\t\ttry {\n\t\t\t\treturn prefs.getLong(keyWrap(key), defValue);\n\t\t\t} catch (ClassCastException e) {\n\t\t\t\t// Return default value\n\t\t\t}\n\t\t}\n\t\treturn defValue;\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/shortcut/ShortcutConfig.java",
    "content": "package com.nononsenseapps.notepad.widget.shortcut;\n\nimport android.content.Intent;\nimport android.content.pm.ShortcutInfo;\nimport android.content.pm.ShortcutManager;\nimport android.database.Cursor;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.AdaptiveIconDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.Icon;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.widget.SimpleCursorAdapter;\nimport android.widget.Spinner;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.content.CursorLoader;\nimport androidx.loader.content.Loader;\n\nimport com.nononsenseapps.helpers.ActivityHelper;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.ThemeHelper;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.activities.main.ActivityMain_;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.database.TaskList;\nimport com.nononsenseapps.notepad.databinding.ActivityShortcutConfigBinding;\n\n\n/**\n * Shows a window to configure the app's smaller widget, letting the user choose which note list\n * will be opened\n */\npublic class ShortcutConfig extends AppCompatActivity {\n\n\t/**\n\t * for {@link R.layout#activity_shortcut_config}\n\t */\n\tprivate ActivityShortcutConfigBinding mBinding;\n\n\t@Override\n\tprotected void onCreate(@Nullable Bundle savedInstanceState) {\n\t\t// Must do this before super.onCreate\n\t\tThemeHelper.setTheme(this);\n\t\tActivityHelper.setSelectedLanguage(this);\n\t\tsuper.onCreate(savedInstanceState);\n\t\tmBinding = ActivityShortcutConfigBinding.inflate(getLayoutInflater());\n\t\tsetContentView(mBinding.getRoot());\n\t\tmBinding.ok.setOnClickListener(x -> onOK());\n\n\t\t// Default result is fail\n\t\tsetResult(RESULT_CANCELED);\n\t\tsetListEntries(mBinding.listSpinner);\n\t}\n\n\t/**\n\t * @return a {@link Bitmap} representing the given {@link Drawable}. Supports also\n\t * {@link AdaptiveIconDrawable}, so you can build a bitmap of an adaptive icon\n\t */\n\t@NonNull\n\tprivate static Bitmap getBitmapFromDrawable(@NonNull Drawable drawable) {\n\t\tfinal Bitmap bmp = Bitmap.createBitmap(\n\t\t\t\tdrawable.getIntrinsicWidth(),\n\t\t\t\tdrawable.getIntrinsicHeight(),\n\t\t\t\tBitmap.Config.ARGB_8888);\n\t\tfinal Canvas canvas = new Canvas(bmp);\n\t\tdrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());\n\t\tdrawable.draw(canvas);\n\t\treturn bmp;\n\t}\n\n\tvoid onOK() {\n\t\t// newer android versions have stricter limits on nested Intents\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n\n\t\t\tShortcutManager shortcutManager = this.getSystemService(ShortcutManager.class);\n\n\t\t\tString shortcutTitle = \"\";\n\t\t\tfinal Intent intent = new Intent();\n\t\t\tif (mBinding.createNoteSwitch.isChecked()) {\n\n\t\t\t\tString listName = null;\n\t\t\t\tfinal Cursor c = (Cursor) mBinding.listSpinner.getSelectedItem();\n\t\t\t\tif (c != null && !c.isClosed() && !c.isAfterLast()) {\n\t\t\t\t\tlistName = c.getString(1);\n\t\t\t\t}\n\t\t\t\tif (listName == null) {\n\t\t\t\t\tNnnLogger.error(ShortcutConfig.class, \"Unexpected null in listName in ShortcutConfig.java\");\n\t\t\t\t}\n\n\t\t\t\tshortcutTitle = ShortcutConfig.this.getString(R.string.title_create) + \" - \" + listName;\n\n\t\t\t\tintent.setClass(ShortcutConfig.this, ActivityMain_.class)\n\t\t\t\t\t\t.setData(Task.URI)\n\t\t\t\t\t\t.setAction(Intent.ACTION_INSERT)\n\t\t\t\t\t\t.putExtra(Task.Columns.DBLIST, mBinding.listSpinner.getSelectedItemId());\n\t\t\t} else {\n\t\t\t\t// this shortcut widget shows a list of notes\n\t\t\t\tfinal Cursor c = (Cursor) mBinding.listSpinner.getSelectedItem();\n\n\t\t\t\tif (c != null && !c.isClosed() && !c.isAfterLast()) {\n\t\t\t\t\tshortcutTitle = c.getString(1);\n\t\t\t\t}\n\t\t\t\tintent.setClass(ShortcutConfig.this, ActivityMain_.class)\n\t\t\t\t\t\t.setAction(Intent.ACTION_VIEW)\n\t\t\t\t\t\t.setData(TaskList.getUri(mBinding.listSpinner.getSelectedItemId()));\n\t\t\t}\n\n\t\t\t// widget IDs must be unique. We use unique titles for all widget combinations\n\t\t\tString shortcutId = shortcutTitle;\n\t\t\tShortcutInfo shortcut = new ShortcutInfo.Builder(this, shortcutId)\n\t\t\t\t\t.setShortLabel(shortcutTitle)\n\t\t\t\t\t.setLongLabel(shortcutTitle)\n\t\t\t\t\t.setIcon(Icon.createWithResource(this, R.drawable.app_icon))\n\t\t\t\t\t.setIntent(intent)\n\t\t\t\t\t.build();\n\n\t\t\tshortcutManager.requestPinShortcut(shortcut, null);\n\t\t\tsetResult(RESULT_OK);\n\t\t\treturn;\n\t\t}\n\n\t\t// legacy code that still works for API 23 and 24\n\t\tfinal Intent shortcutIntent = new Intent();\n\t\t// Set the icon for the shortcut widget\n\t\tDrawable iconDrawable = AppCompatResources.getDrawable(this, R.drawable.app_icon);\n\t\t// we have to give it a bitmap, or else the icon does not appear in simple launcher\n\t\t// https://github.com/SimpleMobileTools/Simple-Launcher\n\t\tshortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, getBitmapFromDrawable(iconDrawable));\n\n\t\tString shortcutTitle = \"\";\n\t\tfinal Intent intent = new Intent();\n\t\tif (mBinding.createNoteSwitch.isChecked()) {\n\t\t\tshortcutTitle = ShortcutConfig.this.getString(R.string.title_create);\n\n\t\t\tintent.setClass(ShortcutConfig.this, ActivityMain_.class)\n\t\t\t\t\t.setData(Task.URI)\n\t\t\t\t\t.setAction(Intent.ACTION_INSERT)\n\t\t\t\t\t.putExtra(Task.Columns.DBLIST, mBinding.listSpinner.getSelectedItemId());\n\t\t} else {\n\t\t\t// this shortcut widget shows a list of notes\n\t\t\tfinal Cursor c = (Cursor) mBinding.listSpinner.getSelectedItem();\n\n\t\t\tif (c != null && !c.isClosed() && !c.isAfterLast()) {\n\t\t\t\tshortcutTitle = c.getString(1);\n\t\t\t}\n\t\t\tshortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME,\n\t\t\t\t\t\"\" + mBinding.listSpinner.getSelectedItem());\n\n\t\t\tintent.setClass(ShortcutConfig.this, ActivityMain_.class)\n\t\t\t\t\t.setAction(Intent.ACTION_VIEW)\n\t\t\t\t\t.setData(TaskList.getUri(mBinding.listSpinner.getSelectedItemId()));\n\t\t}\n\t\tshortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);\n\t\tshortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutTitle);\n\n\t\tsetResult(RESULT_OK, shortcutIntent);\n\n\t\t// Destroy activity\n\t\tfinish();\n\t}\n\n\tprivate void setListEntries(final Spinner listSpinner) {\n\t\tfinal SimpleCursorAdapter mSpinnerAdapter = new SimpleCursorAdapter(\n\t\t\t\tthis, android.R.layout.simple_spinner_dropdown_item, null,\n\t\t\t\tnew String[] { TaskList.Columns.TITLE },\n\t\t\t\tnew int[] { android.R.id.text1 }, 0);\n\n\t\tlistSpinner.setAdapter(mSpinnerAdapter);\n\n\t\tLoaderManager\n\t\t\t\t.getInstance(this)\n\t\t\t\t.restartLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {\n\n\t\t\t\t\t@NonNull\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Loader<Cursor> onCreateLoader(int id, Bundle args) {\n\t\t\t\t\t\treturn new CursorLoader(ShortcutConfig.this,\n\t\t\t\t\t\t\t\tTaskList.URI,\n\t\t\t\t\t\t\t\tnew String[] { TaskList.Columns._ID, TaskList.Columns.TITLE },\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tTaskList.Columns.TITLE);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoadFinished(@NonNull Loader<Cursor> arg0, Cursor c) {\n\t\t\t\t\t\tmSpinnerAdapter.swapCursor(c);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onLoaderReset(@NonNull Loader<Cursor> arg0) {\n\t\t\t\t\t\tmSpinnerAdapter.swapCursor(null);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/DateView.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.text.format.DateFormat;\nimport android.util.AttributeSet;\n\nimport androidx.appcompat.widget.AppCompatTextView;\n\nimport com.nononsenseapps.helpers.TimeFormatter;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\n/**\n * A simple textview that can display time.\n */\npublic class DateView extends AppCompatTextView {\n\n\t// TODO everything in this \"ui\" namespace should be moved to its own gradle module\n\n\tprivate static final int SECONDS_PER_DAY = 3600;\n\n\tSimpleDateFormat mDateFormatter;\n\n\tpublic DateView(Context context) {\n\t\tsuper(context);\n\n\t\t// TODO if you want to also show a \"due time\" on the note, use this instead:\n\t\t//  mDateFormatter = TimeFormatter.getLocalFormatterShort(context);\n\t\t//  as of now we only show the date, which for me is good enough.\n\t\tmDateFormatter = TimeFormatter.getLocalFormatterShortDateOnly(context);\n\t}\n\n\tpublic DateView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\n\t\ttry {\n\t\t\tmDateFormatter = TimeFormatter.getLocalFormatterShortDateOnly(context);\n\t\t} catch (Exception e) {\n\t\t\t// return a simple fallback formatter, just to show something in the view\n\t\t\tmDateFormatter = new SimpleDateFormat(\"E d MMM yyyy, HH:ss\", Locale.US);\n\t\t}\n\t}\n\n\tpublic DateView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\n\t\tmDateFormatter = TimeFormatter.getLocalFormatterShortDateOnly(context);\n\t}\n\n\tpublic void setTimeText(final long time) {\n\t\tsuper.setText(mDateFormatter.format(new Date(time)));\n\t}\n\n\tpublic static CharSequence toDate(String format, long msecs) {\n\t\t// TODO remove this\n\t\t// String format = day;\n\t\ttry {\n\t\t\tCalendar c = Calendar.getInstance(TimeZone.getTimeZone(\"UTC\"));\n\t\t\tc.setTimeInMillis(msecs);\n\n\t\t\treturn DateFormat.format(format, c);\n\t\t} catch (Exception e) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/DelegateFrame.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.RelativeLayout;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.notepad.R;\n\n/**\n * This class is designed to act as a simple version of the touch delegate. E.g.\n * it is intended to enlarge the touch area for a specified child view.\n *\n * Define it entirely in XML as the following example demonstrates:\n *\n * <com.nononsenseapps.ui.DelegateFrame\n * xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n * android:id=\"@+id/datecheckcontainer\"\n * android:layout_width=\"wrap_content\"\n * android:layout_height=\"fill_parent\"\n * android:minWidth=\"44dp\"\n * android:paddingBottom=\"4dp\"\n * android:paddingLeft=\"8dp\"\n * android:paddingRight=\"4dp\"\n * android:paddingTop=\"8dp\"\n * android:clickable=\"true\"\n * app:enlargedView=\"@+id/itemDone\" >\n *\n * It's important to add android:clickable=\"true\" and\n * app:enlargedView=\"@+id/YOURIDHERE\"\n */\npublic class DelegateFrame extends RelativeLayout implements OnClickListener {\n\n\t// TODO is this useless ? it wraps 2 checkboxes. try to delete it\n\n\tprivate final int enlargedViewId;\n\tprivate View cachedView;\n\tprivate static final int UNDEFINED = -1;\n\n\tpublic DelegateFrame(Context context) {\n\t\tthis(context, null);\n\t}\n\n\tpublic DelegateFrame(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic DelegateFrame(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\n\t\t// set values from XML\n\t\tTypedArray a = this.getContext().obtainStyledAttributes(attrs, R.styleable.DelegateFrame);\n\t\tenlargedViewId = a.getResourceId(R.styleable.DelegateFrame_enlargedView, UNDEFINED);\n\t\t// enlargedViewId = attrs.getAttributeResourceValue(\"http://nononsenseapps.com\", \"enlargedView\", UNDEFINED);\n\t\t// NnnLogger.debug(DelegateFrame.class, \"setting xml values! view: \" + enlargedViewId);\n\t\tsetOnClickListener(this);\n\t\ta.recycle();\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tif (cachedView == null && enlargedViewId != UNDEFINED) {\n\t\t\tcachedView = findViewById(enlargedViewId);\n\t\t}\n\t\tNnnLogger.debug(DelegateFrame.class,\n\t\t\t\t\"onTouchEvent! view is null?: \" + (cachedView == null));\n\t\tif (cachedView != null) {\n\t\t\tcachedView.performClick();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ExtraTypesCursorAdapter.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.database.Cursor;\n\nimport com.nononsenseapps.notepad.activities.main.ActivityMain;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\n\n/**\n * It's for something in the drawer in {@link ActivityMain}\n */\npublic class ExtraTypesCursorAdapter extends ExtrasCursorAdapter {\n\n\tprotected final int[] extraTypes;\n\tprotected final int[] extraLayouts;\n\tprotected ArrayList<ArrayList<Object>> extraData = null;\n\n\tprivate final int typeCount;\n\n\t/**\n\t * Extra types should be numbered from 1-length-1. Use 0 if you want the standard layout.\n\t *\n\t * Extra layouts should correspond to type, e.g. index 0 = type 1, index 1 = type 2.\n\t */\n\tpublic ExtraTypesCursorAdapter(Context context, int layout, Cursor c,\n\t\t\t\t\t\t\t\t   String[] from, int[] to, int[] extraIds, int[] extraLabels, int[] extraTypes, int[] extraLayouts) {\n\t\tsuper(context, layout, c, from, to, extraIds, extraLabels, layout);\n\t\tthis.extraTypes = extraTypes;\n\t\tthis.extraLayouts = extraLayouts;\n\n\t\ttypeCount = countTypes();\n\t}\n\n\tprivate int countTypes() {\n\t\tHashSet<Integer> types = new HashSet<>();\n\t\tfor (int type : extraTypes) {\n\t\t\ttypes.add(type);\n\t\t}\n\t\t// Default layout\n\t\ttypes.add(0);\n\t\treturn types.size();\n\t}\n\n\t@Override\n\tpublic int getViewTypeCount() {\n\t\treturn typeCount;\n\t}\n\n\t@Override\n\tpublic int getItemViewType(final int position) {\n\t\tif (position < extraIds.length) {\n\t\t\treturn extraTypes[position];\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tprotected int getItemLayout(final int position) {\n\t\tfinal int type = getItemViewType(position);\n\t\tif (position < extraIds.length && type > 0) {\n\t\t\treturn extraLayouts[type - 1];\n\t\t} else {\n\t\t\treturn layout;\n\t\t}\n\t}\n\n\t@Override\n\tprotected void setExtraText(final ViewHolder viewHolder, final int position) {\n\t\tif (extraData == null || extraData.isEmpty()) {\n\t\t\tsuper.setExtraText(viewHolder, position);\n\t\t}\n\t\t// set all fields\n\t\tfinal List<Object> dataRow = extraData.get(position);\n\t\tObject col;\n\t\tint i;\n\t\tfor (i = 0; i < viewHolder.texts.length && i < dataRow.size(); i++) {\n\t\t\tcol = dataRow.get(i);\n\t\t\tif (col instanceof Integer) {\n\t\t\t\tviewHolder.texts[i].setText(context.getText((Integer) col));\n\t\t\t} else {\n\t\t\t\tviewHolder.texts[i].setText(col.toString());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setExtraData(ArrayList<ArrayList<Object>> extras) {\n\t\tthis.extraData = extras;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ExtrasCursorAdapter.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ResourceCursorAdapter;\nimport android.widget.TextView;\n\n/**\n * Mimics the SimpleCursorAdapter, but also allows extra items to be injected at\n * the beginning. When asked for id for the extra items, the defined ids are returned.\n * Make sure to set them to negative values (< -1) in order not to confuse them with\n * database IDs.\n *\n * @author Jonas\n */\npublic class ExtrasCursorAdapter extends ResourceCursorAdapter {\n\t// private static final String TAG = \"ExtrasCursorAdapter\";\n\n\tprivate Cursor cursor;\n\tprotected final Context context;\n\n\tprotected final int[] extraIds;\n\tprotected final int[] extraLabels;\n\n\tprotected final String[] from;\n\tprotected final int[] to;\n\n\tprotected final int layout;\n\tprotected final int dropdownlayout;\n\n\t/**\n\t * Same as a cursoradapter except two extra arrays are taken (and a layout).\n\t * The first is an array of what IDs you want to assign your items so you\n\t * can identify them later. Second is an array of ids to the String\n\t * resources to use as labels.\n\t */\n\tpublic ExtrasCursorAdapter(Context context, int layout, Cursor c,\n\t\t\t\t\t\t\t   String[] from, int[] to, int[] extraIds, int[] extraLabels, int dropdownlayout) {\n\t\tsuper(context, layout, c, 0);\n\t\tthis.cursor = c;\n\t\tthis.extraIds = extraIds;\n\t\tthis.extraLabels = extraLabels;\n\t\tthis.context = context;\n\t\tthis.from = from;\n\t\tthis.to = to;\n\t\tthis.layout = layout;\n\t\tthis.dropdownlayout = dropdownlayout;\n\t}\n\n\t/**\n\t * Same as a cursoradapter except two extra arrays are taken (and a layout).\n\t * The first is an array of what IDs you want to assign your items so you\n\t * can identify them later. Second is an array of ids to the String\n\t * resources to use as labels.\n\t */\n\tpublic ExtrasCursorAdapter(Context context, int layout, Cursor c,\n\t\t\t\t\t\t\t   int flags, String[] from, int[] to, int[] extraIds,\n\t\t\t\t\t\t\t   int[] extraLabels, int dropdownlayout) {\n\t\tsuper(context, layout, c, flags);\n\t\tthis.cursor = c;\n\t\tthis.extraIds = extraIds;\n\t\tthis.extraLabels = extraLabels;\n\t\tthis.context = context;\n\t\tthis.from = from;\n\t\tthis.to = to;\n\t\tthis.layout = layout;\n\t\tthis.dropdownlayout = dropdownlayout;\n\t}\n\n\t@Override\n\tpublic void bindView(View view, Context context, Cursor cursor) {\n\t\tint i;\n\t\tViewHolder viewHolder = (ViewHolder) view.getTag();\n\t\tif (viewHolder == null) {\n\t\t\tviewHolder = setViewHolder(view);\n\t\t}\n\t\t// Fetch from database\n\t\tfor (i = 0; i < from.length; i++) {\n\t\t\tfinal int colIndex = cursor.getColumnIndex(from[i]);\n\t\t\tString txt = cursor.getString(colIndex);\n\t\t\tviewHolder.texts[i].setText(txt);\n\t\t}\n\t}\n\n\t/**\n\t * Initializes the viewholder according to the specified from/to arrays.\n\t */\n\tprivate ViewHolder setViewHolder(View view) {\n\t\tViewHolder viewHolder = new ViewHolder();\n\t\tviewHolder.texts = new TextView[from.length];\n\t\tint i;\n\t\tfor (i = 0; i < from.length; i++) {\n\t\t\tviewHolder.texts[i] = view.findViewById(to[i]);\n\t\t}\n\t\tview.setTag(viewHolder);\n\t\treturn viewHolder;\n\t}\n\n\t@Override\n\tpublic Cursor swapCursor(Cursor newCursor) {\n\t\tthis.cursor = newCursor;\n\t\treturn super.swapCursor(newCursor);\n\t}\n\n\t@Override\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tif (cursor != null && position >= extraLabels.length)\n\t\t\treturn super.getView(position - extraLabels.length, convertView, parent);\n\n\t\tViewHolder viewHolder = null;\n\t\tif (convertView == null) {\n\t\t\t// Make a new view\n\t\t\tLayoutInflater mInflater = LayoutInflater.from(context);\n\t\t\tconvertView = mInflater.inflate(getItemLayout(position), parent, false);\n\t\t} else {\n\t\t\tviewHolder = (ViewHolder) convertView.getTag();\n\t\t}\n\t\tif (viewHolder == null) {\n\t\t\tviewHolder = setViewHolder(convertView);\n\t\t}\n\t\tsetExtraText(viewHolder, position);\n\n\t\treturn convertView;\n\t}\n\n\t/**\n\t * Only sets the first field\n\t */\n\tprotected void setExtraText(final ViewHolder viewHolder, final int position) {\n\t\tviewHolder.texts[0].setText(context.getText(extraLabels[position]));\n\t}\n\n\t// TODO method to update extra labels\n\n\t@Override\n\tpublic View getDropDownView(int position, View convertView, ViewGroup parent) {\n\t\tif (cursor != null && position >= extraLabels.length)\n\t\t\treturn super.getDropDownView(position - extraLabels.length, convertView, parent);\n\n\t\tViewHolder viewHolder = null;\n\t\tif (convertView == null) {\n\t\t\t// Make a new view\n\t\t\tLayoutInflater mInflater = LayoutInflater.from(context);\n\t\t\tconvertView = mInflater.inflate(dropdownlayout, parent, false);\n\t\t} else {\n\t\t\tviewHolder = (ViewHolder) convertView.getTag();\n\t\t}\n\t\tif (viewHolder == null) {\n\t\t\tviewHolder = setViewHolder(convertView);\n\t\t}\n\t\tsetExtraText(viewHolder, position);\n\n\t\treturn convertView;\n\t}\n\n\tprotected int getItemLayout(final int position) {\n\t\treturn layout;\n\t}\n\n\t@Override\n\tpublic long getItemId(int position) {\n\t\tif (position < extraIds.length) {\n\t\t\treturn extraIds[position];\n\t\t} else {\n\t\t\t// TODO it crashed here in the tests once (tries to open a closed cursor)\n\t\t\t//  what could this function return to represent an INVALID id ?\n\t\t\tlong id = super.getItemId(position - extraIds.length);\n\t\t\treturn id;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Object getItem(int position) {\n\t\tif (position < extraIds.length) {\n\t\t\treturn getExtraItem(position);\n\t\t} else {\n\t\t\treturn super.getItem(position - extraIds.length);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getCount() {\n\t\tif (extraIds != null)\n\t\t\treturn super.getCount() + extraIds.length;\n\t\telse\n\t\t\treturn super.getCount();\n\t}\n\n\t/**\n\t * Should be a number >= count of the wrapped cursor\n\t */\n\tpublic CharSequence getExtraItem(int realPos) {\n\t\tif (extraLabels.length == 0 || realPos < -1 || realPos > extraLabels.length)\n\t\t\treturn null;\n\t\telse\n\t\t\treturn context.getText(extraLabels[realPos]);\n\t}\n\n\tstatic class ViewHolder {\n\t\tTextView[] texts;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/GreyableToggleButton.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\n\nimport androidx.appcompat.widget.AppCompatToggleButton;\nimport androidx.core.content.ContextCompat;\n\nimport com.nononsenseapps.notepad.R;\n\n/**\n * A Checkbox like textview. It displays its 2 states by toggling between 2 text\n * colors: the primary text color and grey. Used in {@link WeekDaysView}\n */\npublic class GreyableToggleButton extends AppCompatToggleButton {\n\n\t// text colors for checked & unchecked status\n\tprivate final ColorStateList primaryColor;\n\tprivate final int secondaryColor;\n\n\t/**\n\t * See also \"GreyableButtonToggle\" in styles.xml\n\t */\n\tpublic GreyableToggleButton(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tprimaryColor = getTextColorPrimary(context);\n\t\tsecondaryColor = ContextCompat.getColor(this.getContext(), R.color.uncheckedGrey);\n\t}\n\n\t/**\n\t * Correct and mandatory way to get the text color for the \"selected\" state\n\t */\n\tprivate static ColorStateList getTextColorPrimary(Context context) {\n\t\tTypedArray a = context\n\t\t\t\t.obtainStyledAttributes(new int[] { android.R.attr.textColorPrimary });\n\t\tColorStateList color = a.getColorStateList(a.getIndex(0));\n\t\ta.recycle();\n\t\treturn color;\n\t}\n\n\t@Override\n\tpublic void setChecked(boolean checked) {\n\t\tsuper.setChecked(checked);\n\t\t// Set correct text color\n\t\tif (checked) {\n\t\t\tsuper.setTextColor(primaryColor);\n\t\t} else {\n\t\t\tsuper.setTextColor(secondaryColor);\n\t\t}\n\t\t// note that the code that reacts to days being pressed in the reminder view is\n\t\t// not here, it's in NotificationItemHelper.java and WeekDaysView.java\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/NoteCheckBox.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.appcompat.widget.AppCompatCheckBox;\n\n/**\n * Convenience class to use in listviews. Bind the id to the checkbox in order to\n * use a onCheckedChangeListener more easily.\n */\npublic class NoteCheckBox extends AppCompatCheckBox {\n\tprivate long noteId = -1;\n\n\tpublic NoteCheckBox(Context context) {\n\t\tsuper(context);\n\t}\n\n\tpublic NoteCheckBox(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t}\n\n\tpublic NoteCheckBox(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t}\n\n\tpublic long getNoteId() {\n\t\treturn noteId;\n\t}\n\n\tpublic void setNoteId(long noteId) {\n\t\tthis.noteId = noteId;\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/NotificationItemHelper.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.app.DatePickerDialog;\nimport android.app.TimePickerDialog;\nimport android.content.Context;\nimport android.view.View;\nimport android.widget.DatePicker;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.database.Notification;\nimport com.nononsenseapps.notepad.database.Task;\nimport com.nononsenseapps.notepad.fragments.TaskDetailFragment;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\n/**\n * Handle setting up all the listeners for a Notification list item\n */\npublic final class NotificationItemHelper {\n\n\tprivate static String getDateString(final Context context, final long time) {\n\t\treturn TimeFormatter.getDateFormatter(context).format(new Date(time));\n\t}\n\n\tprivate static void switchToTime(final View nv) {\n\t\tshowViews(nv.findViewById(R.id.notificationTime),\n\t\t\t\tnv.findViewById(R.id.notificationDate),\n\t\t\t\tnv.findViewById(R.id.notificationTypeTime),\n\t\t\t\tnv.findViewById(R.id.weekdays));\n\n\t\t// hide this view\n\t\tnv.findViewById(R.id.repeatSwitch).setVisibility(View.GONE);\n\t}\n\n\tprivate static void showViews(final View... views) {\n\t\tfor (View v : views) {\n\t\t\tv.setVisibility(View.VISIBLE);\n\t\t}\n\t}\n\n\tprivate static void setTime(final Context context, final Notification not, final Task mTask) {\n\t\tfinal GregorianCalendar cal = TimeFormatter.getLocalCalendar(context);\n\t\t// Start with date, either due date or today (default)\n\t\t// If due date is in the past, default to today + 1hour\n\t\tif (mTask.due != null && mTask.due > cal.getTimeInMillis()) {\n\t\t\tcal.setTimeInMillis(mTask.due);\n\t\t} else {\n\t\t\t// Default to today, set time one hour from now\n\t\t\tcal.add(Calendar.HOUR_OF_DAY, 1);\n\t\t}\n\n\t\t// And set time on notification\n\t\tnot.time = cal.getTimeInMillis();\n\t}\n\n\t/**\n\t * add a new reminder and show it in the note detail page\n\t *\n\t * @param fragment         the {@link TaskDetailFragment} that will host this reminder widget\n\t * @param notificationList the list in {@link TaskDetailFragment} that contains this reminder\n\t *                         widget\n\t */\n\tpublic static void setup(final TaskDetailFragment fragment,\n\t\t\t\t\t\t\t final LinearLayout notificationList, final View nv,\n\t\t\t\t\t\t\t final Notification not, final Task mTask) {\n\t\tswitchToTime(nv);\n\n\t\t// Set time on notification if not set already\n\t\tif (not.time == null) {\n\t\t\tsetTime(fragment.getActivity(), not, mTask);\n\t\t}\n\n\t\t// Set time text\n\t\tfinal TextView notTimeButton = nv.findViewById(R.id.notificationTime);\n\t\tnotTimeButton.setText(not.getLocalTimeText(fragment.getActivity()));\n\n\t\t// Set date text\n\t\tfinal TextView notDateButton = nv.findViewById(R.id.notificationDate);\n\t\tnotDateButton.setText(getDateString(fragment.getActivity(), not.time));\n\n\t\tfinal View notRemoveButton = nv.findViewById(R.id.notificationRemove);\n\n\t\t// Remove button\n\t\tnotRemoveButton.setOnClickListener(v -> {\n\t\t\tif (fragment.isLocked()) {\n\t\t\t\tToast.makeText(fragment.getContext(), R.string.canceled_note_locked,\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Remove row from UI\n\t\t\tnotificationList.removeView((View) v.getParent());\n\t\t\t// Remove from database and renotify\n\t\t\tnot.delete(fragment.getActivity());\n\t\t});\n\n\t\t// Date button\n\t\tnotDateButton.setOnClickListener(v -> {\n\t\t\tif (fragment.isLocked()) {\n\t\t\t\tToast.makeText(fragment.getContext(), R.string.canceled_note_locked,\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinal Calendar localTime = Calendar.getInstance();\n\t\t\tif (not.time != null) {\n\t\t\t\tlocalTime.setTimeInMillis(not.time);\n\t\t\t}\n\n\t\t\tvar onDateSetListnr = new DatePickerDialog.OnDateSetListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void onDateSet(DatePicker dialog, int year, int monthOfYear, int dayOfMonth) {\n\t\t\t\t\tlocalTime.set(Calendar.YEAR, year);\n\t\t\t\t\tlocalTime.set(Calendar.MONTH, monthOfYear);\n\t\t\t\t\tlocalTime.set(Calendar.DAY_OF_MONTH, dayOfMonth);\n\n\t\t\t\t\tnot.time = localTime.getTimeInMillis();\n\t\t\t\t\tnotDateButton.setText(not.getLocalDateText(fragment.getActivity()));\n\t\t\t\t\tnot.save(fragment.getActivity(), true);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// configure and show a popup with a date-picker calendar view\n\t\t\tfinal DatePickerDialog datedialog = new DatePickerDialog(\n\t\t\t\t\tfragment.requireContext(),\n\t\t\t\t\tonDateSetListnr,\n\t\t\t\t\tlocalTime.get(Calendar.YEAR),\n\t\t\t\t\tlocalTime.get(Calendar.MONTH),\n\t\t\t\t\tlocalTime.get(Calendar.DAY_OF_MONTH));\n\t\t\tdatedialog.setTitle(R.string.select_date);\n\n\t\t\tdatedialog.show();\n\t\t});\n\n\t\t// Time button\n\t\tnotTimeButton.setOnClickListener(v -> {\n\t\t\tif (fragment.isLocked()) {\n\t\t\t\tToast.makeText(fragment.getContext(), R.string.canceled_note_locked,\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Display time picker\n\t\t\tfinal Calendar localTime = Calendar.getInstance();\n\t\t\tif (not.time != null) {\n\t\t\t\tlocalTime.setTimeInMillis(not.time);\n\t\t\t}\n\n\t\t\tTimePickerDialog.OnTimeSetListener onTimeSetListener = (view, hourOfDay, minute) -> {\n\t\t\t\tlocalTime.set(Calendar.HOUR_OF_DAY, hourOfDay);\n\t\t\t\tlocalTime.set(Calendar.MINUTE, minute);\n\t\t\t\tnot.time = localTime.getTimeInMillis();\n\t\t\t\tnotTimeButton.setText(not.getLocalTimeText(fragment.getActivity()));\n\t\t\t\tnot.save(fragment.getActivity(), true);\n\t\t\t};\n\n\t\t\tfinal TimePickerDialog timedialog = fragment\n\t\t\t\t\t.getTimePickerDialog(localTime, onTimeSetListener);\n\t\t\ttimedialog.setTitle(R.string.time);\n\t\t\ttimedialog.show();\n\t\t});\n\n\t\t// week days button strip\n\t\tWeekDaysView days = nv.findViewById(R.id.weekdays);\n\t\tdays.setCheckedDays(not.repeats);\n\t\tdays.setOnCheckedDaysChangedListener(checkedDays -> {\n\t\t\tif (fragment.isLocked()) {\n\t\t\t\tToast.makeText(fragment.getContext(), R.string.canceled_note_locked,\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\treturn false; // return the button to the previous state\n\t\t\t}\n\t\t\tnot.repeats = checkedDays;\n\t\t\tnot.saveInBackground(fragment.getActivity(), true);\n\t\t\treturn true; // all ok, it can proceed\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ShowcaseHelper.java",
    "content": "package com.nononsenseapps.ui;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.widget.Toolbar;\nimport androidx.fragment.app.FragmentActivity;\n\nimport com.getkeepsafe.taptargetview.TapTarget;\nimport com.getkeepsafe.taptargetview.TapTargetView;\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.helpers.ThemeHelper;\n\n/**\n * Holds all code related to the showcase view, which for now is provided\n * by the {@link TapTargetView} library\n */\npublic final class ShowcaseHelper {\n\n\t/**\n\t * Create, configure and show a view to highlight the overflow menu, using a library.\n\t * The view is shown above the given {@link FragmentActivity} and features a\n\t * title and a short description\n\t */\n\tpublic static void showForOverflowMenu(@NonNull FragmentActivity activity,\n\t\t\t\t\t\t\t\t\t\t   @StringRes int titleStringId,\n\t\t\t\t\t\t\t\t\t\t   @StringRes int descriptionStringId) {\n\t\t// get the toolbar from the activity\n\t\tToolbar tBar = activity.findViewById(androidx.appcompat.R.id.action_bar);\n\n\t\t// always a good idea to check\n\t\tif (tBar == null) {\n\t\t\tNnnLogger.error(ShowcaseHelper.class,\n\t\t\t\t\t\"Can't show the TapTargetView, the Toolbar is unavailable\");\n\t\t\treturn;\n\t\t}\n\n\t\tvar target2 = TapTarget.forToolbarOverflow(tBar,\n\t\t\t\tactivity.getString(titleStringId), activity.getString(descriptionStringId));\n\n\t\tfinishConfiguringAndShow(target2, activity);\n\t}\n\n\t/**\n\t * All functions in this class share this common configuration for the TapTargetView, but to\n\t * highlight a {@link View} on the {@link FragmentActivity} you would implement an alternative\n\t * to {@link #showForOverflowMenu}\n\t */\n\tprivate static void finishConfiguringAndShow(TapTarget target, FragmentActivity activity) {\n\t\t// TODO can *you* make it prettier ? See also https://github.com/KeepSafe/TapTargetView\n\t\ttarget.outerCircleAlpha(0.9f)\n\t\t\t\t.drawShadow(true)\n\t\t\t\t.cancelable(true) // tap outside the circle to dismiss the showcaseView\n\t\t\t\t.tintTarget(false)\n\t\t\t\t// TODO not good for Material YOU themes. shows green text on a green circle, for example\n\t\t\t\t.textColorInt(ThemeHelper.getThemeAccentColor(activity));\n\n\t\t// this listener will always dismiss the taptargetview, regardless of where you click.\n\t\t// It's less frustrating to use, and above all it makes the espresso tests work.\n\t\tvar listener = new TapTargetView.Listener() {\n\n\t\t\t@Override\n\t\t\tpublic void onTargetClick(TapTargetView view) {\n\t\t\t\tview.dismiss(true);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onTargetLongClick(TapTargetView view) {\n\t\t\t\tthis.onTargetClick(view);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onTargetCancel(TapTargetView view) {\n\t\t\t\tview.dismiss(false);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onOuterCircleClick(TapTargetView view) {\n\t\t\t\t// this is probably the only important method to override, the rest is boilerplate\n\t\t\t\tview.dismiss(false);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onTargetDismissed(TapTargetView view, boolean userInitiated) {\n\t\t\t\t// don't .dismiss(), because that causes a NullPointerException\n\t\t\t\t// in android.view.View.dispatchDetachedFromWindow(). See\n\t\t\t\t// https://github.com/KeepSafe/TapTargetView/issues/395#issuecomment-987952528\n\t\t\t\tview.setVisibility(View.GONE);\n\t\t\t\tsuper.onTargetDismissed(view, userInitiated);\n\t\t\t}\n\t\t};\n\n\t\tTapTargetView.showFor(activity, target, listener);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/StyledEditText.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.text.Editable;\nimport android.text.Layout;\nimport android.text.Selection;\nimport android.text.Spannable;\nimport android.text.TextWatcher;\nimport android.text.method.ArrowKeyMovementMethod;\nimport android.text.style.ClickableSpan;\nimport android.text.style.RelativeSizeSpan;\nimport android.text.style.StyleSpan;\nimport android.text.style.TypefaceSpan;\nimport android.text.util.Linkify;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport androidx.appcompat.widget.AppCompatEditText;\n\nimport com.nononsenseapps.notepad.R;\n\n/**\n * An EditText field that highlights the first line and makes links clickable in\n * the text. The text is still selectable, movable etc.\n */\npublic class StyledEditText extends AppCompatEditText {\n\n\tObject titleStyleSpan;\n\tObject titleSizeSpan;\n\tObject titleFamilySpan;\n\tObject bodyFamilySpan;\n\n\tprivate final float mTitleRelativeSize;\n\tprivate boolean mLinkify;\n\tprivate boolean mTitleLarger = true;\n\n\tpublic StyledEditText(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tTypedArray a = context\n\t\t\t\t.getTheme()\n\t\t\t\t.obtainStyledAttributes(attrs, R.styleable.StyledTextView, 0, 0);\n\n\t\tint mTitleFontFamily;\n\t\tint mBodyFontFamily;\n\t\tint mTitleFontStyle;\n\t\ttry {\n\t\t\tmTitleRelativeSize = a\n\t\t\t\t\t.getFloat(R.styleable.StyledTextView_titleRelativeSize, 1.0f);\n\t\t\tmTitleFontFamily = a\n\t\t\t\t\t.getInteger(R.styleable.StyledTextView_titleFontFamily, 0);\n\t\t\tmTitleFontStyle = a\n\t\t\t\t\t.getInteger(R.styleable.StyledTextView_titleFontStyle, 0);\n\t\t\tmLinkify = a\n\t\t\t\t\t.getBoolean(R.styleable.StyledTextView_linkify, false);\n\t\t\tmBodyFontFamily = a\n\t\t\t\t\t.getInteger(R.styleable.StyledTextView_bodyFontFamily, 0);\n\t\t} finally {\n\t\t\ta.recycle();\n\t\t}\n\n\t\tsetTitleRelativeLarger(mTitleLarger);\n\t\tsetTitleFontFamily(mTitleFontFamily);\n\t\tsetTitleFontStyle(mTitleFontStyle);\n\t\tsetBodyFontFamily(mBodyFontFamily);\n\n\t\t// Style on change\n\t\taddTextChangedListener(new TextWatcher() {\n\n\t\t\t@Override\n\t\t\tpublic void onTextChanged(CharSequence s, int start, int before, int count) {}\n\n\t\t\t@Override\n\t\t\tpublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}\n\n\t\t\t@Override\n\t\t\tpublic void afterTextChanged(Editable s) {\n\t\t\t\tspannify(s);\n\t\t\t\tif (mLinkify) {\n\t\t\t\t\tLinkify.addLinks(StyledEditText.this, Linkify.ALL);\n\t\t\t\t\t// Links shouldnt steal click focus\n\t\t\t\t\t// But text must still be selectable etc\n\t\t\t\t\tsetMovementMethod(new ArrowKeyMovementMethod());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * @param larger true will use layout defined size, else same size\n\t */\n\tpublic void setTitleRelativeLarger(final boolean larger) {\n\t\tmTitleLarger = larger;\n\t\ttitleSizeSpan = new RelativeSizeSpan(larger ? mTitleRelativeSize : 1.0f);\n\t}\n\n\t/**\n\t * @param family matches order defined in xml\n\t */\n\tpublic void setTitleFontFamily(final int family) {\n\t\tswitch (family) {\n\t\t\tcase 1 -> titleFamilySpan = new TypefaceSpan(\"sans-serif-condensed\");\n\t\t\tcase 2 -> titleFamilySpan = new TypefaceSpan(\"sans-serif-light\");\n\t\t\tcase 3 -> titleFamilySpan = new TypefaceSpan(\"sans-serif-thin\");\n\t\t\tdefault -> titleFamilySpan = new TypefaceSpan(\"sans-serif\");\n\t\t}\n\t}\n\n\t/**\n\t * @param style matches order defined in xml\n\t */\n\tpublic void setTitleFontStyle(final int style) {\n\t\tswitch (style) {\n\t\t\tcase 1 -> titleStyleSpan = new StyleSpan(android.graphics.Typeface.BOLD);\n\t\t\tcase 2 -> titleStyleSpan = new StyleSpan(android.graphics.Typeface.ITALIC);\n\t\t\tdefault -> titleStyleSpan = new StyleSpan(android.graphics.Typeface.NORMAL);\n\t\t}\n\t}\n\n\t/**\n\t * @param family matches order defined in xml\n\t */\n\tpublic void setBodyFontFamily(final int family) {\n\t\tswitch (family) {\n\t\t\tcase 1 -> bodyFamilySpan = new TypefaceSpan(\"sans-serif-condensed\");\n\t\t\tcase 2 -> bodyFamilySpan = new TypefaceSpan(\"sans-serif-light\");\n\t\t\tcase 3 -> bodyFamilySpan = new TypefaceSpan(\"sans-serif-thin\");\n\t\t\tdefault -> bodyFamilySpan = new TypefaceSpan(\"sans-serif\");\n\t\t}\n\t}\n\n\t/**\n\t * @param size 0, 1 or 2 representing small/medium/large\n\t */\n\tpublic void setTheTextSize(final int size) {\n\t\tswitch (size) {\n\t\t\tcase 0 ->\n\t\t\t\t// small\n\t\t\t\t\tsuper.setTextSize(14.0f);\n\t\t\tcase 2 ->\n\t\t\t\t// large\n\t\t\t\t\tsuper.setTextSize(22.0f);\n\t\t\tdefault ->\n\t\t\t\t// medium\n\t\t\t\t\tsuper.setTextSize(18.0f);\n\t\t}\n\t}\n\n\t/**\n\t * @param clickable if links should be clickable\n\t */\n\tpublic void setLinkify(final boolean clickable) {\n\t\tthis.mLinkify = clickable;\n\t}\n\n\tprivate void spannify(final Spannable s) {\n\t\t// Clear this first, or it will multiply!\n\t\tfor (RelativeSizeSpan rs : s.getSpans(0, s.length(),\n\t\t\t\tRelativeSizeSpan.class)) {\n\t\t\ts.removeSpan(rs);\n\t\t}\n\n\t\tint titleEnd = s.toString().indexOf(\"\\n\");\n\t\tif (titleEnd < 0) {\n\t\t\ttitleEnd = s.toString().length();\n\t\t}\n\n\t\tif (titleEnd > 0) {\n\t\t\ts.setSpan(titleStyleSpan, 0, titleEnd,\n\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\ts.setSpan(titleSizeSpan, 0, titleEnd,\n\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\ts.setSpan(titleFamilySpan, 0, titleEnd,\n\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\n\t\t\tif (titleEnd < s.toString().length()) {\n\t\t\t\ts.setSpan(bodyFamilySpan, titleEnd, s.toString().length(),\n\t\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * either opens the note or opens a link in the browser. It does not call\n\t * {@link View#performClick()} because there's no need to, the default\n\t * behavior is fine.\n\t */\n\t@SuppressLint(\"ClickableViewAccessibility\")\n\t@Override\n\tpublic boolean onTouchEvent(MotionEvent event) {\n\t\tif (this.getText() == null) {\n\t\t\treturn super.onTouchEvent(event);\n\t\t}\n\n\t\tSpannable buffer = this.getText();\n\n\t\tint action = event.getAction();\n\t\tif (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {\n\t\t\tint x = (int) event.getX();\n\t\t\tint y = (int) event.getY();\n\n\t\t\tx -= this.getTotalPaddingLeft();\n\t\t\ty -= this.getTotalPaddingTop();\n\n\t\t\tx += this.getScrollX();\n\t\t\ty += this.getScrollY();\n\n\t\t\tLayout layout = this.getLayout();\n\t\t\tint line = layout.getLineForVertical(y);\n\t\t\tint off = layout.getOffsetForHorizontal(line, x);\n\n\t\t\tClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);\n\n\t\t\t// Cant click to the right of a span, if the line ends with the span!\n\t\t\tif (x > layout.getLineRight(line)) {\n\t\t\t\t// Don't call the span\n\t\t\t} else if (link.length != 0) {\n\t\t\t\tif (action == MotionEvent.ACTION_UP) {\n\t\t\t\t\t// TODO the same click trick of TitleNoteTextview.java\n\t\t\t\t\tlink[0].onClick(this);\n\t\t\t\t} else if (action == MotionEvent.ACTION_DOWN) {\n\t\t\t\t\tSelection.setSelection(buffer,\n\t\t\t\t\t\t\tbuffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn super.onTouchEvent(event);\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/TitleNoteTextView.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.annotation.SuppressLint;\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.TypedArray;\nimport android.net.Uri;\nimport android.text.Layout;\nimport android.text.Selection;\nimport android.text.Spannable;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.style.ClickableSpan;\nimport android.text.style.RelativeSizeSpan;\nimport android.text.style.StyleSpan;\nimport android.text.style.TypefaceSpan;\nimport android.text.style.URLSpan;\nimport android.text.util.Linkify;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport androidx.appcompat.widget.AppCompatTextView;\n\nimport com.nononsenseapps.helpers.NnnLogger;\nimport com.nononsenseapps.notepad.R;\n\n/**\n * A TextView that highlights the first line and makes links clickable. The text\n * is not selectable. This is intended to be used in a ListView where the text\n * on items is not intended to be selectable.\n */\npublic class TitleNoteTextView extends AppCompatTextView {\n\n\tObject titleStyleSpan;\n\tObject titleSizeSpan;\n\tObject titleFamilySpan;\n\tObject bodyFamilySpan;\n\n\tprivate final int primaryColor;\n\tprivate final int secondaryColor;\n\tprivate int mBodyFontFamily;\n\tprivate int mTitleFontFamily;\n\tprivate int mTitleFontStyle;\n\tprivate boolean mLinkify;\n\tprivate String mStyledText;\n\tprivate String mTitle = \"\";\n\tprivate String mRest = \"\";\n\n\tpublic TitleNoteTextView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\n\t\tTypedArray attributes = context\n\t\t\t\t.getTheme()\n\t\t\t\t.obtainStyledAttributes(attrs, R.styleable.StyledTextView, 0, 0);\n\n\t\tfloat mTitleRelativeSize;\n\t\ttry {\n\t\t\tmTitleRelativeSize = attributes\n\t\t\t\t\t.getFloat(R.styleable.StyledTextView_titleRelativeSize, 1.0f);\n\t\t\tmTitleFontFamily = attributes\n\t\t\t\t\t.getInteger(R.styleable.StyledTextView_titleFontFamily, 0);\n\t\t\tmTitleFontStyle = attributes\n\t\t\t\t\t.getInteger(R.styleable.StyledTextView_titleFontStyle, 0);\n\t\t\tmBodyFontFamily = attributes\n\t\t\t\t\t.getInteger(R.styleable.StyledTextView_bodyFontFamily, 0);\n\t\t\tmStyledText = attributes.getString(R.styleable.StyledTextView_styledText);\n\t\t\tmLinkify = attributes.getBoolean(R.styleable.StyledTextView_linkify, false);\n\n\t\t\tprimaryColor = super.getCurrentTextColor();\n\t\t\tsecondaryColor = attributes.getColor(R.styleable.StyledTextView_secondaryColor, primaryColor);\n\t\t} finally {\n\t\t\tattributes.recycle();\n\t\t}\n\n\t\ttitleSizeSpan = new RelativeSizeSpan(mTitleRelativeSize);\n\n\t\tsetTitleFontFamily(mTitleFontFamily);\n\n\t\tsetTitleFontStyle(mTitleFontStyle);\n\n\t\tsetBodyFontFamily(mBodyFontFamily);\n\n\t}\n\n\t/**\n\t * Useful method for widgets where only default textviews may be used. Use\n\t * this method in your widget adapter to get the functionality this view\n\t * would have offered.\n\t * <p/>\n\t * First argument is a relative size of the first line, like 1.3\n\t * <p/>\n\t * Second argument is of type: android.graphics.Typeface.BOLD, ITALIC etc\n\t * <p/>\n\t * Third is: 0 (normal), 1 (condensed), 2 (light), 3 (thin)\n\t */\n\tpublic static CharSequence getStyledText(final String title,\n\t\t\t\t\t\t\t\t\t\t\t final String rest, final float titleRelSize, final int face,\n\t\t\t\t\t\t\t\t\t\t\t final int font) {\n\t\tfinal StringBuilder textBuilder = new StringBuilder(title);\n\t\tif (!rest.isEmpty()) {\n\t\t\ttextBuilder.append(\"\\n\").append(rest);\n\t\t}\n\t\treturn getStyledText(textBuilder.toString(), titleRelSize, face, font);\n\t}\n\n\t/**\n\t * Useful method for widgets where only default textviews may be used. Use\n\t * this method in your widget adapter to get the functionality this view\n\t * would have offered.\n\t * <p/>\n\t * First argument is a relative size of the first line, like 1.3\n\t * <p/>\n\t * Second argument is of type: android.graphics.Typeface.BOLD, ITALIC etc\n\t * <p/>\n\t * Third is: 0 (normal), 1 (condensed), 2 (light), 3 (thin)\n\t */\n\tpublic static CharSequence getStyledText(final String text,\n\t\t\t\t\t\t\t\t\t\t\t final float titleRelSize, final int face, final int font) {\n\t\tif (text == null) return null;\n\n\t\tint titleEnd = text.indexOf(\"\\n\");\n\t\tif (titleEnd < 0) {\n\t\t\ttitleEnd = text.length();\n\t\t}\n\n\t\tTypefaceSpan fontSpan = switch (font) {\n\t\t\tcase 1 -> new TypefaceSpan(\"sans-serif-condensed\");\n\t\t\tcase 2 -> new TypefaceSpan(\"sans-serif-light\");\n\t\t\tcase 3 -> new TypefaceSpan(\"sans-serif-thin\");\n\t\t\tdefault -> new TypefaceSpan(\"sans-serif\");\n\t\t};\n\n\t\tSpannableString ss = new SpannableString(text);\n\t\tif (titleEnd > 0) {\n\t\t\tss.setSpan(new StyleSpan(face), 0, titleEnd,\n\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\tss.setSpan(new RelativeSizeSpan(titleRelSize), 0, titleEnd,\n\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\tss.setSpan(fontSpan, 0, titleEnd,\n\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t}\n\n\t\treturn ss;\n\t}\n\n\t/**\n\t * @param family matches order defined in xml\n\t */\n\tpublic void setTitleFontFamily(final int family) {\n\t\tif (mTitleFontFamily == family)\n\t\t\treturn;\n\n\t\tmTitleFontFamily = family;\n\n\t\tswitch (family) {\n\t\t\tcase 1 -> titleFamilySpan = new TypefaceSpan(\"sans-serif-condensed\");\n\t\t\tcase 2 -> titleFamilySpan = new TypefaceSpan(\"sans-serif-light\");\n\t\t\tcase 3 -> titleFamilySpan = new TypefaceSpan(\"sans-serif-thin\");\n\t\t\tdefault -> titleFamilySpan = new TypefaceSpan(\"sans-serif\");\n\t\t}\n\t}\n\n\t/**\n\t * @param style matches order defined in xml\n\t */\n\tpublic void setTitleFontStyle(final int style) {\n\t\tif (mTitleFontStyle == style)\n\t\t\treturn;\n\n\t\tmTitleFontStyle = style;\n\n\t\tswitch (style) {\n\t\t\tcase 1 -> titleStyleSpan = new StyleSpan(android.graphics.Typeface.BOLD);\n\t\t\tcase 2 -> titleStyleSpan = new StyleSpan(android.graphics.Typeface.ITALIC);\n\t\t\tdefault -> titleStyleSpan = new StyleSpan(android.graphics.Typeface.NORMAL);\n\t\t}\n\t}\n\n\t/**\n\t * @param family matches order defined in xml\n\t */\n\tpublic void setBodyFontFamily(final int family) {\n\t\tif (mBodyFontFamily == family)\n\t\t\treturn;\n\n\t\tmBodyFontFamily = family;\n\n\t\tswitch (family) {\n\t\t\tcase 1 -> bodyFamilySpan = new TypefaceSpan(\"sans-serif-condensed\");\n\t\t\tcase 2 -> bodyFamilySpan = new TypefaceSpan(\"sans-serif-light\");\n\t\t\tcase 3 -> bodyFamilySpan = new TypefaceSpan(\"sans-serif-thin\");\n\t\t\tdefault -> bodyFamilySpan = new TypefaceSpan(\"sans-serif\");\n\t\t}\n\t}\n\n\tpublic void useSecondaryColor(final boolean useSecondary) {\n\t\tif (secondaryColor != primaryColor) {\n\t\t\tif (useSecondary) {\n\t\t\t\tsuper.setTextColor(secondaryColor);\n\t\t\t} else {\n\t\t\t\tsuper.setTextColor(primaryColor);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic String getStyledText() {\n\t\treturn mStyledText;\n\t}\n\n\tpublic void setStyledText(final String styledText) {\n\t\tif (styledText != null) {\n\t\t\tthis.mStyledText = styledText;\n\n\t\t\ttry {\n\t\t\t\tint titleEnd = mStyledText.indexOf(\"\\n\");\n\t\t\t\tif (titleEnd < 0) {\n\t\t\t\t\ttitleEnd = mStyledText.length();\n\t\t\t\t}\n\n\t\t\t\t// Need to link first so we can avoid the title\n\t\t\t\tif (titleEnd > 0) {\n\t\t\t\t\tSpannableString text = new SpannableString(mStyledText);\n\t\t\t\t\ttext.setSpan(titleStyleSpan, 0, titleEnd,\n\t\t\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\t\t\ttext.setSpan(titleSizeSpan, 0, titleEnd,\n\t\t\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\t\t\ttext.setSpan(titleFamilySpan, 0, titleEnd,\n\t\t\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\n\t\t\t\t\tif (titleEnd < mStyledText.length()) {\n\t\t\t\t\t\ttext.setSpan(bodyFamilySpan, titleEnd, mStyledText.length(),\n\t\t\t\t\t\t\t\tSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\t\t\t\t}\n\n\t\t\t\t\tsetText(text, BufferType.SPANNABLE);\n\n\t\t\t\t\tif (mLinkify) {\n\t\t\t\t\t\t// this makes the links clickable in the notes in the lists.\n\t\t\t\t\t\tLinkify.addLinks(this, Linkify.ALL);\n\n\t\t\t\t\t\t// Make sure links dont steal click focus everywhere\n\t\t\t\t\t\tsetMovementMethod(null);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Emtpy string\n\t\t\t\t\tsetText(new SpannableString(mStyledText), BufferType.SPANNABLE);\n\t\t\t\t}\n\t\t\t} catch (NullPointerException miuibug) {\n\t\t\t\t/*\n\t\t\t\t * A bug reported on Miui Android 4.4. NullPointerException inside setText method\n\t\t\t\t * due to some nullpointer in android.text.SpannableStringInternal.equals method.\n\t\t\t\t * See crash log in issue #291\n\t\t\t\t */\n\t\t\t\tsetText(mStyledText);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * either opens the note or opens a link in the browser. It does not call\n\t * {@link View#performClick()} because there's no need to, the default\n\t * behavior is fine.\n\t */\n\t@SuppressLint(\"ClickableViewAccessibility\")\n\t@Override\n\tpublic boolean onTouchEvent(MotionEvent event) {\n\t\tif (!(this.getText() instanceof Spanned)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tint action = event.getAction();\n\t\tif (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_DOWN) {\n\t\t\treturn false;\n\t\t}\n\n\t\tint x = (int) event.getX();\n\t\tint y = (int) event.getY();\n\n\t\tx -= this.getTotalPaddingLeft();\n\t\ty -= this.getTotalPaddingTop();\n\n\t\tx += this.getScrollX();\n\t\ty += this.getScrollY();\n\n\t\tLayout layout = this.getLayout();\n\t\tint line = layout.getLineForVertical(y);\n\t\tint off = layout.getOffsetForHorizontal(line, x);\n\n\t\tSpannable buffer = (Spannable) this.getText();\n\t\tClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);\n\n\t\t// Cant click to the right of a span, if the line ends with the span!\n\t\tif (x > layout.getLineRight(line)) {\n\t\t\t// Don't call the span\n\t\t} else if (link.length != 0) {\n\t\t\tswitch (action) {\n\t\t\t\tcase MotionEvent.ACTION_UP ->\n\t\t\t\t\t// the user touched a link => fire a custom onClick() method\n\t\t\t\t\t\tonClickableSpanClicked(link[0]);\n\t\t\t\tcase MotionEvent.ACTION_DOWN ->\n\t\t\t\t\t// select text\n\t\t\t\t\t\tSelection.setSelection(buffer,\n\t\t\t\t\t\t\t\tbuffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));\n\t\t\t\tdefault -> {\n\t\t\t\t}\n\t\t\t\t// 100% impossible to reach this.\n\t\t\t}\n\n\t\t\t// ONLY in this case we can say that the event was handled\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\n\t/**\n\t * When the user clicks on a link (in a {@link ClickableSpan}) this view will fire an intent\n\t * to open the browser, using this function\n\t *\n\t * @param cs the {@link ClickableSpan} that the user clicked\n\t */\n\tprivate void onClickableSpanClicked(ClickableSpan cs) {\n\t\tif (cs instanceof URLSpan) {\n\t\t\t// it's 99% similar to the code on URLSpan.java in the Android SDK, but they set\n\t\t\t// Browser.EXTRA_APPLICATION_ID, which tells the browser to open links always on\n\t\t\t// the same tab. By NOT setting it, every clicked link will be opened in a new\n\t\t\t// browser tab, which I prefer.\n\t\t\t// TODO If anyone reading this dislikes this behavior, we can add a setting to the\n\t\t\t//  preferences page, just open an issue on github and explain\n\t\t\tUri uri = Uri.parse(((URLSpan) cs).getURL());\n\t\t\tIntent intent = new Intent(Intent.ACTION_VIEW, uri);\n\n\t\t\ttry {\n\t\t\t\tthis.getContext().startActivity(intent);\n\t\t\t} catch (ActivityNotFoundException e) {\n\t\t\t\tNnnLogger.warning(TitleNoteTextView.class,\n\t\t\t\t\t\t\"Could not find a browser to open the url: \" + uri.toString());\n\t\t\t}\n\t\t} else {\n\t\t\tNnnLogger.warning(TitleNoteTextView.class, \"ClickableSpan was not an UrlSpan\");\n\t\t\t// it should not happen. Anyway, just call the old code from version 5.7.1\n\t\t\tcs.onClick(this);\n\t\t}\n\t}\n\n\tpublic String getTextRest() {\n\t\treturn mRest;\n\t}\n\n\tpublic void setTextRest(final String rest) {\n\t\tif (rest != null) {\n\t\t\tthis.mRest = rest;\n\t\t\t// Make sure it starts with a new line\n\t\t\tif (!mRest.isEmpty()) {\n\t\t\t\tmRest = (rest.startsWith(\"\\n\") ? \"\" : \"\\n\") + rest;\n\t\t\t}\n\n\t\t\tsetStyledText(mTitle + mRest);\n\t\t}\n\t}\n\n\tpublic String getTextTitle() {\n\t\treturn mTitle;\n\t}\n\n\tpublic void setTextTitle(final String title) {\n\t\tif (title != null) {\n\t\t\t// Make sure it does not end with a newline\n\t\t\tthis.mTitle = (title.endsWith(\"\\n\") ? title.substring(0,\n\t\t\t\t\ttitle.length() - 1) : title);\n\n\t\t\tsetStyledText(mTitle + mRest);\n\t\t}\n\t}\n\n\t/**\n\t * @param clickable if links should be clickable\n\t */\n\tpublic void setLinkify(final boolean clickable) {\n\t\tthis.mLinkify = clickable;\n\t}\n\n\t/**\n\t * @param size 0, 1 or 2 representing small/medium/large\n\t */\n\tpublic void setTheTextSize(final int size) {\n\t\tswitch (size) {\n\t\t\t// small\n\t\t\tcase 0 -> super.setTextSize(12.0f);\n\t\t\t// large\n\t\t\tcase 2 -> super.setTextSize(18.0f);\n\t\t\t// medium\n\t\t\tdefault -> super.setTextSize(14.0f);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ViewsHelper.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.util.TypedValue;\n\npublic final class ViewsHelper {\n\n\t/**\n\t * Convert DPs to Pixels for the current screen density\n\t */\n\tpublic static int convertDip2Pixels(Context context, int dip) {\n\t\treturn (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,\n\t\t\t\tdip, context.getResources().getDisplayMetrics());\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/WeekDaysView.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.ui;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\nimport com.nononsenseapps.helpers.ActivityHelper;\nimport com.nononsenseapps.helpers.TimeFormatter;\nimport com.nononsenseapps.notepad.R;\nimport com.nononsenseapps.notepad.databinding.WeekdaysLayoutBinding;\n\nimport java.text.SimpleDateFormat;\nimport java.util.GregorianCalendar;\nimport java.util.Locale;\n\n/**\n * Show a row of 7 days, each can be toggled ON & OFF\n */\npublic class WeekDaysView extends LinearLayout {\n\n\tpublic interface onCheckedDaysChangeListener {\n\t\t/**\n\t\t * react to these days being selected\n\t\t *\n\t\t * @return FALSE if the action was rejected, indicating that you have to put back the\n\t\t * day button in its former state, or TRUE if the action succeeded\n\t\t */\n\t\tboolean onChange(long checkedDays);\n\t}\n\n\tpublic static final int mon = 0x1;\n\tpublic static final int tue = 0x10;\n\tpublic static final int wed = 0x100;\n\tpublic static final int thu = 0x1000;\n\tpublic static final int fri = 0x10000;\n\tpublic static final int sat = 0x100000;\n\tpublic static final int sun = 0x1000000;\n\n\tGreyableToggleButton monday;\n\tGreyableToggleButton tuesday;\n\tGreyableToggleButton wednesday;\n\tGreyableToggleButton thursday;\n\tGreyableToggleButton friday;\n\tGreyableToggleButton saturday;\n\tGreyableToggleButton sunday;\n\n\t/**\n\t * for {@link R.layout#weekdays_layout}\n\t */\n\tWeekdaysLayoutBinding mBinding;\n\n\tprivate onCheckedDaysChangeListener listener = null;\n\tprivate Locale mLocale;\n\n\tpublic WeekDaysView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tLayoutInflater mInflater = context.getSystemService(LayoutInflater.class);\n\t\tmInflater.inflate(R.layout.weekdays_layout, this, true);\n\t\t// TODO use view bindings also here ?\n\t\t// mBinding = WeekdaysLayoutBinding.inflate(mInflater, this, true);\n\n\t\t// TO DO (useless): respect locale settings regarding first day of week\n\n\t\ttry {\n\t\t\tmLocale = ActivityHelper.getUserLocale(context);\n\n\t\t\tSimpleDateFormat dayFormat = TimeFormatter.getLocalFormatterWeekdayShort(context);\n\t\t\t// 2013-05-13 was a monday\n\t\t\tGregorianCalendar gc = new GregorianCalendar(2013,\n\t\t\t\t\tGregorianCalendar.MAY, 13);\n\t\t\tfinal long base = gc.getTimeInMillis();\n\t\t\tfinal long day = 24 * 60 * 60 * 1000;\n\n\t\t\tmonday = findViewById(R.id.day1); // mBinding.day1\n\t\t\tinitializeToggleButton(dayFormat.format(gc.getTime()), monday);\n\n\t\t\ttuesday = findViewById(R.id.day2);\n\t\t\tgc.setTimeInMillis(base + 1 * day);\n\t\t\tinitializeToggleButton(dayFormat.format(gc.getTime()), tuesday);\n\n\t\t\twednesday = findViewById(R.id.day3);\n\t\t\tgc.setTimeInMillis(base + 2 * day);\n\t\t\tinitializeToggleButton(dayFormat.format(gc.getTime()), wednesday);\n\n\t\t\tthursday = findViewById(R.id.day4);\n\t\t\tgc.setTimeInMillis(base + 3 * day);\n\t\t\tinitializeToggleButton(dayFormat.format(gc.getTime()), thursday);\n\n\t\t\tfriday = findViewById(R.id.day5);\n\t\t\tgc.setTimeInMillis(base + 4 * day);\n\t\t\tinitializeToggleButton(dayFormat.format(gc.getTime()), friday);\n\n\t\t\tsaturday = findViewById(R.id.day6);\n\t\t\tgc.setTimeInMillis(base + 5 * day);\n\t\t\tinitializeToggleButton(dayFormat.format(gc.getTime()), saturday);\n\n\t\t\tsunday = findViewById(R.id.day7);\n\t\t\tgc.setTimeInMillis(base + 6 * day);\n\t\t\tinitializeToggleButton(dayFormat.format(gc.getTime()), sunday);\n\t\t} catch (Exception e) {\n\t\t\t// For UI editor's sake\n\t\t\tmLocale = Locale.getDefault();\n\t\t}\n\n\t}\n\n\tvoid initializeToggleButton(final String text, final GreyableToggleButton button) {\n\t\tbutton.setText(text.toUpperCase(mLocale));\n\t\tbutton.setTextOn(text.toUpperCase(mLocale));\n\t\tbutton.setTextOff(text.toUpperCase(mLocale));\n\t\tbutton.setOnClickListener(this::onDayButtonClicked);\n\t}\n\n\tpublic long getCheckedDays() {\n\t\tlong checkedDays = 0;\n\n\t\tif (monday.isChecked()) checkedDays |= mon;\n\t\tif (tuesday.isChecked()) checkedDays |= tue;\n\t\tif (wednesday.isChecked()) checkedDays |= wed;\n\t\tif (thursday.isChecked()) checkedDays |= thu;\n\t\tif (friday.isChecked()) checkedDays |= fri;\n\t\tif (saturday.isChecked()) checkedDays |= sat;\n\t\tif (sunday.isChecked()) checkedDays |= sun;\n\n\t\treturn checkedDays;\n\t}\n\n\tpublic void setCheckedDays(long checkedDays) {\n\t\tmonday.setChecked(0 < (checkedDays & mon));\n\t\ttuesday.setChecked(0 < (checkedDays & tue));\n\t\twednesday.setChecked(0 < (checkedDays & wed));\n\t\tthursday.setChecked(0 < (checkedDays & thu));\n\t\tfriday.setChecked(0 < (checkedDays & fri));\n\t\tsaturday.setChecked(0 < (checkedDays & sat));\n\t\tsunday.setChecked(0 < (checkedDays & sun));\n\t}\n\n\t/**\n\t * Reacts to one of the 7 {@link GreyableToggleButton} being touched (== \"toggled\").\n\t * onCheckedChanged(CompoundButton, boolean) can't be used because it would call\n\t * {@link GreyableToggleButton#setChecked} in an infinite loop. Note that before\n\t * calling this, Android calls {@link GreyableToggleButton#setChecked}, so here we\n\t * can either confirm or undo the \"checking\" action.\n\t */\n\tprivate void onDayButtonClicked(View v) {\n\t\tif (listener == null) return;\n\t\tboolean Ok = listener.onChange(getCheckedDays());\n\t\tvar btn = (GreyableToggleButton) v;\n\t\tif (!Ok) {\n\t\t\t// the listener returned false, to indicate that the operation\n\t\t\t// was rejected => revert the toggle button to its former state.\n\t\t\t// Don't worry: doing this does NOT touch the data of the note\n\t\t\t// for the database.\n\t\t\tbtn.toggle();\n\t\t}\n\t}\n\n\t/**\n\t * set the function to call when one of the 7 buttons is clicked.\n\t * See {@link onCheckedDaysChangeListener}\n\t */\n\tpublic void setOnCheckedDaysChangedListener(onCheckedDaysChangeListener listener) {\n\t\tthis.listener = listener;\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/res/anim/activity_slide_in_right.xml",
    "content": "<!--\n Copyright (C) 2013 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:duration=\"@android:integer/config_mediumAnimTime\"\n\tandroid:fromXDelta=\"-50%p\"\n\tandroid:toXDelta=\"0\"/>\n"
  },
  {
    "path": "app/src/main/res/anim/activity_slide_out_right_full.xml",
    "content": "<!--\n Copyright (C) 2013 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:duration=\"@android:integer/config_mediumAnimTime\"\n\tandroid:fromXDelta=\"0\"\n\tandroid:toXDelta=\"100%p\"/>\n"
  },
  {
    "path": "app/src/main/res/anim/cycle_7.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n\n-->\n<cycleInterpolator xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:cycles=\"3\"/>\n"
  },
  {
    "path": "app/src/main/res/anim/shake.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n\n-->\n<!-- password popups will shake if you write the wrong password. This animation is used -->\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:duration=\"@android:integer/config_longAnimTime\"\n\tandroid:fromXDelta=\"0\"\n\tandroid:interpolator=\"@anim/cycle_7\"\n\tandroid:toXDelta=\"10\"/>\n"
  },
  {
    "path": "app/src/main/res/anim/slide_in_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromYDelta=\"50%p\"\n\t\tandroid:toYDelta=\"0\"/>\n\t<alpha android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromAlpha=\"0.0\"\n\t\tandroid:toAlpha=\"1.0\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/slide_in_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromYDelta=\"-50%p\"\n\t\tandroid:toYDelta=\"0\"/>\n\t<alpha android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromAlpha=\"0.0\"\n\t\tandroid:toAlpha=\"1.0\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/slide_out_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromYDelta=\"0\"\n\t\tandroid:toYDelta=\"50%p\"/>\n\t<alpha android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromAlpha=\"1.0\"\n\t\tandroid:toAlpha=\"0.0\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/slide_out_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromYDelta=\"0\"\n\t\tandroid:toYDelta=\"-50%p\"/>\n\t<alpha android:duration=\"@android:integer/config_mediumAnimTime\"\n\t\tandroid:fromAlpha=\"1.0\"\n\t\tandroid:toAlpha=\"0.0\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/drawable/btn_toggle_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n\n<!-- background of a button of the 'week days' view -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- TODO use android:background=\"?android:attr/selectableItemBackground\" instead ?? -->\n\t<item android:id=\"@android:id/background\">\n\t\t<selector>\n\t\t\t<!-- show a grey background when the user touches\n\t\t\t or holds the finger on the view -->\n\t\t\t<item android:drawable=\"@color/widget_pressed_background\"\n\t\t\t\tandroid:state_pressed=\"true\"/>\n\n\t\t\t<!-- default fallback background is invisible,\n\t\t\t for when the user's finger is not on this widget -->\n\t\t\t<item android:drawable=\"@android:color/transparent\"/>\n\n\t\t</selector>\n\t</item>\n\n\t<item android:id=\"@android:id/toggle\">\n\t\t<selector>\n\n\t\t\t<!-- underline the text when the togglebutton\n\t\t\tis in the ON state -->\n\t\t\t<item\n\t\t\t\tandroid:drawable=\"@drawable/ic_underline_accent\"\n\t\t\t\tandroid:state_checked=\"true\"/>\n\n\t\t\t<!-- default fallback background is invisible,\n\t\t\t for the OFF state -->\n\t\t\t<item android:drawable=\"@android:color/transparent\"/>\n\n\t\t</selector>\n\t</item>\n\n</layer-list>"
  },
  {
    "path": "app/src/main/res/drawable/folder_move_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- https://materialdesignicons.com/ -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M9,18V15H5V11H9V8L14,13M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/folder_plus_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- https://materialdesignicons.com/ -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M10,4L12,6H20A2,2 0 0,1 22,8V18A2,2 0 0,1 20,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10M15,9V12H12V14H15V17H17V14H20V12H17V9H15Z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_add_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_alarm_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8H11v6l4.75,2.85 0.75,-1.23 -4,-2.37V8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_alarm_add_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm1,-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_archive_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M3,3H21V7H3V3M4,8H20V21H4V8M9.5,11A0.5,0.5 0 0,0 9,11.5V13H15V11.5A0.5,0.5 0 0,0 14.5,11H9.5Z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_brightness_6_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_check_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_checkbox_checked.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<path android:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M10,17L5,12L6.41,10.58L10,14.17L17.59,6.58L19,8M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z\" />\n\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_checkbox_unchecked.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<path android:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z\" />\n\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_clear_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_copy_24dp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"/>\n\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_delete_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_export.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\t<path android:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M9 12H18.8L16.3 9.5L17.7 8.1L22.6 13L17.7 17.9L16.3 16.5L18.8 14H9V12M21 17.4V20H3V6H21V8.6L23 10.6V4C23 2.9 22.1 2 21 2H3C1.9 2 1 2.9 1 4V20C1 21.1 1.9 22 3 22H21C22.1 22 23 21.1 23 20V15.4L21 17.4Z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_folder_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_help_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,17h-2v-2h2v2zm2.07,-7.75l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2H8c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_import.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\t<path android:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M1 12H10.8L8.3 9.5L9.7 8.1L14.6 13L9.7 17.9L8.3 16.5L10.8 14H1V12M21 2H3C1.9 2 1 2.9 1 4V10.1H3V6H21V20H3V16H1V20C1 21.1 1.9 22 3 22H21C22.1 22 23 21.1 23 20V4C23 2.9 22.1 2 21 2\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_info_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_lock_closed_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm-6,-5.1c1.71,0 3.1,1.39 3.1,3.1v2H9V6h-0.1c0,-1.71 1.39,-3.1 3.1,-3.1zM18,20H6V10h12v10zm-6,-3c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_lock_open_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm6,-9h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm0,12H6V10h12v10z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_notebook_minus_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M3 7V5H5V4C5 2.9 5.9 2 7 2H13V9L15.5 7.5L18 9V2H19C20 2 21 3 21 4V13.8C20.1 13.3 19.1 13 18 13C14.7 13 12 15.7 12 19C12 20.1 12.3 21.1 12.8 22H7C5.9 22 5 21 5 20V19H3V17H5V13H3V11H5V7H3M5 5V7H7V5H5M5 19H7V17H5V19M5 13H7V11H5V13M14 18V20H22V18H14Z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_refresh_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_sd_storage_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-6,6h-2V4h2v4zm3,0h-2V4h2v4zm3,0h-2V4h2v4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_search_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zm-6,0C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_select_all.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M9,9H15V15H9M7,17H17V7H7M15,5H17V3H15M15,21H17V19H15M19,17H21V15H19M19,9H21V7H19M19,21A2,2 0 0,0 21,19H19M19,13H21V11H19M11,21H13V19H11M9,3H7V5H9M3,17H5V15H3M5,21V19H3A2,2 0 0,0 5,21M19,3V5H21A2,2 0 0,0 19,3M13,3H11V5H13M3,9H5V7H3M7,21H9V19H7M3,13H5V11H3M3,5H5V3A2,2 0 0,0 3,5Z\"/>\n\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_settings_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_share_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_sort_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015. Jonas Kalderstam\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M3,18h6v-2H3v2zM3,6v2h18V6H3zm0,7h12v-2H3v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_stat_notification_edit.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<!-- this is the drawable associated with the notification -->\n\n\t<path\n\t\tandroid:fillColor=\"#FFF\"\n\t\tandroid:pathData=\"M16.84,2.73C16.45,2.73 16.07,2.88 15.77,3.17L13.65,5.29L18.95,10.6L21.07,8.5C21.67,7.89 21.67,6.94 21.07,6.36L17.9,3.17C17.6,2.88 17.22,2.73 16.84,2.73M12.94,6L4.84,14.11L7.4,14.39L7.58,16.68L9.86,16.85L10.15,19.41L18.25,11.3M4.25,15.04L2.5,21.73L9.2,19.94L8.96,17.78L6.65,17.61L6.47,15.29\"/>\n\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_underline_accent.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<!-- a rectangle, near the bottom half of the picture,\n\t that uses the theme's accent color. So it picks up\n\t the accent color chosen by the user for material YOU\n\t themes in android 13. To see it in the preview panel\n\t of android studio use \"ThemeBaseDark\" in the style\n\t spinner on the corner -->\n\n\t<!-- TODO it should be less wide and more thin, in my\n\t      opinion. Look for something to copy on\n\t      https://materialdesignicons.com/\n\t      maybe \"format-underline\" ? -->\n\n\t<path android:fillColor=\"?colorAccent\"\n\t\tandroid:pathData=\"M21,5M3,21V19H21V21H3Z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_undo_24dp.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24.0\"\n\tandroid:viewportHeight=\"24.0\">\n\t<path\n\t\tandroid:fillColor=\"#FFFFFF\"\n\t\tandroid:pathData=\"M12.5,8c-2.65,0 -5.05,0.99 -6.9,2.6L2,7v9h9l-3.62,-3.62c1.39,-1.16 3.16,-1.88 5.12,-1.88 3.54,0 6.55,2.31 7.6,5.5l2.37,-0.78C21.08,11.03 17.15,8 12.5,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/img_default_selector_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2010 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item android:drawable=\"@drawable/list_pressed_holo_dark\" android:state_pressed=\"true\"/>\n\t<item android:drawable=\"@drawable/list_activated_holo\" android:state_enabled=\"true\" android:state_focused=\"true\"/>\n\t<item android:drawable=\"@drawable/list_activated_holo\" android:state_activated=\"true\"/>\n\t<item android:drawable=\"@drawable/transparent\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/img_default_selector_light.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2010 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item android:drawable=\"@drawable/list_pressed_holo_dark\" android:state_pressed=\"true\"/>\n\t<item android:drawable=\"@drawable/list_activated_holo\" android:state_enabled=\"true\" android:state_focused=\"true\"/>\n\t<item android:drawable=\"@drawable/transparent\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_blackclassic_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item android:drawable=\"@drawable/list_activated_holo\" android:state_activated=\"true\"/>\n\t<item android:drawable=\"@android:color/black\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_darkcard_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- android:drawable=\"@color/light_selected_item_color\" -->\n\t<item\n\t\tandroid:drawable=\"@drawable/list_activated_holo\"\n\t\tandroid:state_activated=\"true\"/>\n\t<!--\n\tTODO this background for a selectable note is defined\n\t in a png file. can you make an XML drawable ? or simply\n\t use a plain color that you define in colors.xml\n\t -->\n\t<item android:drawable=\"@drawable/card_dark\"/> <!-- android:drawable=\"@android:color/transparent\" -->\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_lightcard_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item android:drawable=\"@drawable/list_activated_holo\" android:state_activated=\"true\"/>\n\t<item android:drawable=\"@drawable/card_light\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_lightclassic_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item android:drawable=\"@drawable/list_activated_holo\" android:state_activated=\"true\"/>\n\t<item android:drawable=\"@color/background_holo_light\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable-anydpi-v26/app_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<!-- This is an adaptive icon for android API >= 26. The background is a gradient and the\n\t foreground is a symbol (a vector drawable). See their xml files to edit them separately -->\n\t<background android:drawable=\"@drawable/icon_gradient\"/>\n\t<foreground android:drawable=\"@drawable/icon_foreground_symbol\"/>\n\t<monochrome android:drawable=\"@drawable/icon_monochrome\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/drawable-anydpi-v26/icon_foreground_symbol.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<!-- The foreground is a symbol downloaded from https://materialdesignicons.com/\n\t  Copy only the 'path' tag -->\n\n\t<group android:scaleX=\"0.4\"\n\t\tandroid:scaleY=\"0.4\"\n\t\tandroid:translateX=\"7\"\n\t\tandroid:translateY=\"9\">\n\n\t\t<path android:fillColor=\"#777\"\n\t\t\tandroid:pathData=\"M16.84,2.73C16.45,2.73 16.07,2.88 15.77,3.17L13.65,5.29L18.95,10.6L21.07,8.5C21.67,7.89 21.67,6.94 21.07,6.36L17.9,3.17C17.6,2.88 17.22,2.73 16.84,2.73M12.94,6L4.84,14.11L7.4,14.39L7.58,16.68L9.86,16.85L10.15,19.41L18.25,11.3M4.25,15.04L2.5,21.73L9.2,19.94L8.96,17.78L6.65,17.61L6.47,15.29\"/>\n\t</group>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable-anydpi-v26/icon_gradient.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:aapt=\"http://schemas.android.com/aapt\"\n\tandroid:width=\"200dp\"\n\tandroid:height=\"200dp\"\n\tandroid:viewportWidth=\"200\"\n\tandroid:viewportHeight=\"200\">\n\n\t<!-- This drawable is a gradient to show behind the symbol in the app's icon.\n\t See https://stackoverflow.com/a/58043191/6307322 -->\n\n\t<path android:pathData=\"M0,200l0,-200l200,0l0,200z\">\n\t\t<aapt:attr name=\"android:fillColor\">\n\t\t\t<gradient\n\t\t\t\tandroid:endY=\"200\"\n\t\t\t\tandroid:startY=\"0\"\n\t\t\t\tandroid:type=\"linear\">\n\n\t\t\t\t<!-- The following tags decide the colors of the gradient, from top to bottom. 'offset' is\n\t\t\t\t a number from 0 (TOP) to 1 (BOTTOM) that represents the coordinate where the 'color' is\n\t\t\t\t  centered. To add a color, simply add an 'item' tag with the 2 fields -->\n\n\t\t\t\t<item\n\t\t\t\t\tandroid:color=\"#FF9010\"\n\t\t\t\t\tandroid:offset=\"0.3\"/>\n\t\t\t\t<item\n\t\t\t\t\tandroid:color=\"#80947E\"\n\t\t\t\t\tandroid:offset=\"0.31\"/>\n\t\t\t\t<item\n\t\t\t\t\tandroid:color=\"#A2C3D0\"\n\t\t\t\t\tandroid:offset=\"0.35\"/>\n\t\t\t\t<item\n\t\t\t\t\tandroid:color=\"#FEFEFE\"\n\t\t\t\t\tandroid:offset=\"0.36\"/>\n\t\t\t\t<item\n\t\t\t\t\tandroid:color=\"#E8E8E8\"\n\t\t\t\t\tandroid:offset=\"0.5\"/>\n\t\t\t</gradient>\n\t\t</aapt:attr>\n\t</path>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable-anydpi-v26/icon_monochrome.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:width=\"24dp\"\n\tandroid:height=\"24dp\"\n\tandroid:viewportWidth=\"24\"\n\tandroid:viewportHeight=\"24\">\n\n\t<group android:scaleX=\"0.5\"\n\t\tandroid:scaleY=\"0.5\"\n\t\tandroid:translateX=\"6\"\n\t\tandroid:translateY=\"6\">\n\n\t\t<!-- the monochrome icon (for android 13 and greater)\n\tshows only the pencil. Android replaces the 'fillColor'\n\t according to the user-set theme -->\n\n\t\t<path\n\t\t\tandroid:fillColor=\"#FFF\"\n\t\t\tandroid:pathData=\"M16.84,2.73C16.45,2.73 16.07,2.88 15.77,3.17L13.65,5.29L18.95,10.6L21.07,8.5C21.67,7.89 21.67,6.94 21.07,6.36L17.9,3.17C17.6,2.88 17.22,2.73 16.84,2.73M12.94,6L4.84,14.11L7.4,14.39L7.58,16.68L9.86,16.85L10.15,19.41L18.25,11.3M4.25,15.04L2.5,21.73L9.2,19.94L8.96,17.78L6.65,17.61L6.47,15.29\"/>\n\n\t</group>\n</vector>"
  },
  {
    "path": "app/src/main/res/layout/actionbar_custom_view_done.xml",
    "content": "<!--\n  Copyright 2012 Roman Nurik\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:divider=\"?android:attr/dividerVertical\"\n\tandroid:dividerPadding=\"12dp\"\n\tandroid:orientation=\"horizontal\"\n\tandroid:showDividers=\"end\">\n\n\t<include layout=\"@layout/actionbar_done_button\"/>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/actionbar_custom_view_done_discard.xml",
    "content": "<!--\n  Copyright 2012 Roman Nurik\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:divider=\"?android:attr/dividerVertical\"\n\tandroid:dividerPadding=\"12dp\"\n\tandroid:orientation=\"horizontal\"\n\tandroid:showDividers=\"middle\">\n\n\t<include layout=\"@layout/actionbar_discard_button\"/>\n\n\t<include layout=\"@layout/actionbar_done_button\"/>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/actionbar_discard_button.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- used in the time machine (note history) activity -->\n<FrameLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\tandroid:id=\"@+id/actionbar_discard\"\n\tstyle=\"?android:actionButtonStyle\"\n\tandroid:layout_width=\"0dp\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:layout_weight=\"1\">\n\n\t<TextView\n\t\tstyle=\"?android:actionBarTabTextStyle\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_gravity=\"center\"\n\t\tandroid:drawablePadding=\"8dp\"\n\t\tandroid:gravity=\"center_vertical\"\n\t\tandroid:paddingRight=\"20dp\"\n\t\tandroid:text=\"@android:string/cancel\"\n\t\tapp:drawableLeftCompat=\"@drawable/ic_clear_24dp\"/>\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/actionbar_done_button.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<FrameLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\tandroid:id=\"@+id/actionbar_done\"\n\tstyle=\"?android:actionButtonStyle\"\n\tandroid:layout_width=\"0dp\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:layout_weight=\"1\">\n\n\t<TextView\n\t\tstyle=\"?android:actionBarTabTextStyle\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_gravity=\"center\"\n\t\tapp:drawableLeftCompat=\"@drawable/ic_check_24dp\"\n\t\tandroid:drawablePadding=\"8dp\"\n\t\tandroid:gravity=\"center_vertical\"\n\t\tandroid:paddingRight=\"20dp\"\n\t\tandroid:text=\"@string/done\"/>\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_dashclock_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n\n<androidx.fragment.app.FragmentContainerView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:id=\"@+id/fragment\"\n\tandroid:name=\"com.nononsenseapps.notepad.dashclock.DashclockPrefsFragment\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:fitsSystemWindows=\"true\"/>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<androidx.drawerlayout.widget.DrawerLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:id=\"@+id/drawerLayout\"\n\tandroid:fitsSystemWindows=\"true\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\ttools:context=\".activities.main.ActivityMain\">\n\n\t<!-- The main content view -->\n\n\t<FrameLayout\n\t\tandroid:id=\"@+id/fragment1\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"match_parent\"/>\n\n\t<!-- The navigation drawer -->\n\n\t<include\n\t\tandroid:id=\"@+id/leftDrawer\"\n\t\tlayout=\"@layout/drawer_layout\"\n\t\tandroid:layout_width=\"320dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_gravity=\"start\"/>\n\n</androidx.drawerlayout.widget.DrawerLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:fitsSystemWindows=\"true\"\n\tandroid:orientation=\"horizontal\">\n\n\t<!-- Contains the \"main menu\" fragment for the settings activity -->\n\n\t<androidx.fragment.app.FragmentContainerView\n\t\tandroid:id=\"@+id/fragment\"\n\t\tandroid:name=\"com.nononsenseapps.notepad.prefs.IndexPrefs\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"match_parent\"/>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_shortcut_config.xml",
    "content": "<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:paddingTop=\"8dp\"\n\tandroid:orientation=\"vertical\"\n\tandroid:fitsSystemWindows=\"true\"\n\ttools:context=\".widget.shortcut.ShortcutConfig\">\n\n\t<TextView\n\t\tandroid:id=\"@+id/textView1\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginLeft=\"16dp\"\n\t\tandroid:layout_marginTop=\"2dp\"\n\t\tandroid:layout_marginRight=\"16dp\"\n\t\tandroid:layout_marginBottom=\"2dp\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:text=\"@string/settings_list\"\n\t\tandroid:textAppearance=\"@android:style/TextAppearance.Holo.Medium\"\n\t\tandroid:textColor=\"?android:attr/textColorPrimary\"/>\n\n\t<Spinner\n\t\tandroid:id=\"@+id/listSpinner\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\tandroid:layout_marginLeft=\"16dp\"\n\t\tandroid:layout_marginTop=\"2dp\"\n\t\tandroid:layout_marginRight=\"16dp\"\n\t\tandroid:layout_marginBottom=\"2dp\"\n\t\tandroid:prompt=\"@string/settings_list_dialog\"\n\t\tandroid:spinnerMode=\"dropdown\"\n\t\ttools:listitem=\"@android:layout/simple_spinner_dropdown_item\"/>\n\n\t<androidx.appcompat.widget.SwitchCompat\n\t\tandroid:id=\"@+id/createNoteSwitch\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\tandroid:layout_marginLeft=\"16dp\"\n\t\tandroid:layout_marginTop=\"20dp\"\n\t\tandroid:layout_marginRight=\"16dp\"\n\t\tandroid:layout_marginBottom=\"2dp\"\n\t\tandroid:text=\"@string/title_create\"\n\t\tandroid:textAppearance=\"@android:style/TextAppearance.Holo.Medium\"\n\t\tandroid:textColor=\"?android:attr/textColorPrimary\"/>\n\n\t<TextView\n\t\tandroid:id=\"@+id/textView2\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginLeft=\"16dp\"\n\t\tandroid:layout_marginTop=\"2dp\"\n\t\tandroid:layout_marginRight=\"16dp\"\n\t\tandroid:layout_marginBottom=\"2dp\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:text=\"@string/shortcut_help1\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"/>\n\n\t<Button\n\t\tandroid:id=\"@+id/ok\"\n\t\tandroid:layout_marginTop=\"30dp\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_gravity=\"center_horizontal\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:text=\"@android:string/ok\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_task_history.xml",
    "content": "<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:custom=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:fitsSystemWindows=\"true\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:layout_marginLeft=\"@dimen/activity_lone_horizontal_margin\"\n\tandroid:layout_marginRight=\"@dimen/activity_lone_horizontal_margin\"\n\tandroid:orientation=\"vertical\"\n\tandroid:paddingLeft=\"@dimen/activity_lone_horizontal_padding\"\n\tandroid:paddingTop=\"@dimen/activity_lone_vertical_margin\"\n\tandroid:paddingRight=\"@dimen/activity_lone_horizontal_padding\"\n\tandroid:paddingBottom=\"@dimen/activity_lone_vertical_margin\"\n\ttools:context=\".activities.ActivityTaskHistory\">\n\n\t<TextView\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:text=\"@string/drag_to_timetravel\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceLarge\"\n\t\tandroid:layout_margin=\"4dp\"\n\t\tandroid:textColor=\"@color/accent\"/>\n\n\t<SeekBar\n\t\tandroid:id=\"@+id/seekBar\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginTop=\"8dp\"\n\t\tandroid:layout_marginBottom=\"8dp\"/>\n\n\t<TextView\n\t\tandroid:id=\"@+id/timestamp\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginBottom=\"4dp\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:text=\"@string/drag_to_timetravel\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\tandroid:textColor=\"?android:attr/textColorSecondary\"/>\n\n\t<ScrollView\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:fillViewport=\"true\">\n\n\t\t<com.nononsenseapps.ui.TitleNoteTextView\n\t\t\tandroid:id=\"@+id/taskText\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:ellipsize=\"end\"\n\t\t\tandroid:paddingBottom=\"4dp\"\n\t\t\tandroid:singleLine=\"false\"\n\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceSmall\"\n\t\t\tandroid:textColor=\"?android:attr/textColorPrimary\"\n\t\t\tandroid:background=\"?attr/list_item_card_background\"\n\t\t\tandroid:textIsSelectable=\"true\"\n\t\t\tcustom:linkify=\"true\"\n\t\t\tandroid:layout_margin=\"10dp\"\n\t\t\tandroid:padding=\"10dp\"\n\t\t\tcustom:titleFontFamily=\"robotocondensed\"\n\t\t\tcustom:titleFontStyle=\"bold\"\n\t\t\tcustom:titleRelativeSize=\"1.4\"/>\n\t</ScrollView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_widget_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:fitsSystemWindows=\"true\"\n\tandroid:orientation=\"@integer/layout_best_orientation\">\n\n\t<include\n\t\tandroid:id=\"@+id/widgetPreviewWrapper\"\n\t\tlayout=\"@layout/activity_widget_config_part_preview\"\n\t\tandroid:layout_width=\"@dimen/widget_conf_preview_width\"\n\t\tandroid:layout_height=\"@dimen/widget_conf_preview_height\"\n\t\tandroid:layout_gravity=\"center_horizontal\"/>\n\n\t<include\n\t\tandroid:id=\"@+id/widget_conf_wrapper\"\n\t\tlayout=\"@layout/activity_widget_config_part_settings\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"match_parent\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_widget_config_part_preview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"@dimen/widget_conf_preview_width\"\n\tandroid:layout_height=\"@dimen/widget_conf_preview_height\"\n\tandroid:layout_gravity=\"center_horizontal\"\n\tandroid:background=\"@null\"\n\tandroid:paddingLeft=\"10dp\"\n\tandroid:paddingTop=\"10dp\"\n\tandroid:paddingRight=\"10dp\"\n\tandroid:paddingBottom=\"10dp\"\n\ttools:ignore=\"Overdraw\">\n\n\t<include\n\t\tandroid:id=\"@+id/widget_preview\"\n\t\tlayout=\"@layout/widget_layout\"/>\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_widget_config_part_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:id=\"@+id/list_widget_conf_wrapper\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:background=\"@color/googlenow_darkergrey\"\n\tandroid:fillViewport=\"true\"\n\tandroid:isScrollContainer=\"true\"\n\tandroid:scrollbars=\"vertical\"\n\ttools:context=\"com.nononsenseapps.notepad.widget.list.ListWidgetConfig\"\n\ttools:ignore=\"Overdraw\">\n\n\t<LinearLayout\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginTop=\"4dp\"\n\t\tandroid:orientation=\"vertical\"\n\t\tandroid:paddingHorizontal=\"16dp\"\n\t\tandroid:paddingBottom=\"16dp\"\n\t\ttools:context=\"com.nononsenseapps.notepad.widget.list.ListWidgetConfig\">\n\n\t\t<Spinner\n\t\t\tandroid:id=\"@+id/listSpinner\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:prompt=\"@string/settings_list_dialog\"\n\t\t\tandroid:spinnerMode=\"dropdown\"\n\t\t\ttools:listitem=\"@android:layout/simple_spinner_dropdown_item\"/>\n\n\t\t<Spinner\n\t\t\tandroid:id=\"@+id/sortingSpinner\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:entries=\"@array/sorting_preference\"\n\t\t\tandroid:spinnerMode=\"dropdown\"\n\t\t\ttools:listitem=\"@android:layout/simple_spinner_dropdown_item\"/>\n\n\t\t<Spinner\n\t\t\tandroid:id=\"@+id/themeSpinner\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:entries=\"@array/widget_theme_preference\"\n\t\t\tandroid:spinnerMode=\"dropdown\"\n\t\t\ttools:listitem=\"@android:layout/simple_spinner_dropdown_item\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:labelFor=\"@+id/itemRowsSeekBar\"\n\t\t\tandroid:paddingLeft=\"16dp\"\n\t\t\tandroid:text=\"@string/transparency\"\n\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceSmall\"\n\t\t\tandroid:textStyle=\"normal\"/>\n\n\t\t<SeekBar\n\t\t\tandroid:id=\"@+id/transparencySeekBar\"\n\t\t\tstyle=\"?android:attr/seekBarStyle\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:max=\"100\"\n\t\t\tandroid:progress=\"75\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:labelFor=\"@+id/itemRowsSeekBar\"\n\t\t\tandroid:paddingLeft=\"16dp\"\n\t\t\tandroid:text=\"@string/item_max_height\"\n\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceSmall\"\n\t\t\tandroid:textStyle=\"normal\"/>\n\n\t\t<SeekBar\n\t\t\tandroid:id=\"@+id/itemRowsSeekBar\"\n\t\t\tstyle=\"?android:attr/seekBarStyle\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:max=\"10\"\n\t\t\tandroid:progress=\"3\"/>\n\n\t\t<CheckBox\n\t\t\tandroid:id=\"@+id/hideCheckBox\"\n\t\t\tstyle=\"?android:attr/checkboxStyle\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\t\tandroid:text=\"@string/hide_checkbox\"/>\n\n\t\t<CheckBox\n\t\t\tandroid:id=\"@+id/hideDateCheckBox\"\n\t\t\tstyle=\"?android:attr/checkboxStyle\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\t\tandroid:text=\"@string/hide_date\"/>\n\n\t\t<CheckBox\n\t\t\tandroid:id=\"@+id/transparentHeaderCheckBox\"\n\t\t\tstyle=\"?android:attr/checkboxStyle\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\t\tandroid:text=\"@string/hide_header\"/>\n\n\t\t<CheckBox\n\t\t\tandroid:id=\"@+id/showCompletedCheckBox\"\n\t\t\tstyle=\"?android:attr/checkboxStyle\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"@dimen/list_item_min_size\"\n\t\t\tandroid:text=\"@string/show_completed_notes\"/>\n\n\t</LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/app_pref_about_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<ScrollView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\ttools:ignore=\"HardcodedText\">\n\n\t<!-- Represents the \"about\" page in the settings -->\n\n\t<LinearLayout\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:orientation=\"vertical\"\n\t\tandroid:paddingLeft=\"@dimen/preference_screen_side_margin\"\n\t\tandroid:paddingTop=\"8dp\"\n\t\tandroid:paddingBottom=\"4dp\">\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:text=\"Copyright (&#169;) 2014 Jonas Kalderstam\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"@string/nononsense_notes\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:id=\"@+id/appVersionRow\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"@string/version\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"@string/app_about_dev_team\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"@string/app_about_git_repo\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"@string/app_about_license\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"@string/app_about_translations\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"@string/app_about_bugreports\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:id=\"@+id/tvDonations\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"8dp\"\n\t\t\tandroid:text=\"filled in AboutPrefs.java\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"16dp\"\n\t\t\tandroid:text=\"@string/libraries_used\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n\t\t\tandroid:textColor=\"?colorAccent\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:text=\"DragSortListView (&#169;) Carl Bauer\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:text=\"TapTargetView (&#169;) Keepsafe Software Inc.\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:text=\"AndroidAnnotations (&#169;) the AndroidAnnotations project\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:text=\"Retrofit (&#169;) Square, Inc.\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:text=\"orgparser (&#169;) Jonas Kalderstam\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:autoLink=\"all\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:text=\"joda-time (&#169;) Joda org.\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t</LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/app_pref_password_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<ScrollView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\">\n\n\t<!-- Represents the settings page dedicated to the password -->\n\n\t<LinearLayout\n\t\tandroid:layout_width=\"fill_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:orientation=\"vertical\"\n\t\tandroid:paddingTop=\"8dp\"\n\t\tandroid:paddingBottom=\"4dp\">\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_margin=\"8dp\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:text=\"@string/password_info\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<!-- This list will be the normal preferences -->\n\t\t<!--\n\t\t\t <ListView\n\t\t\tandroid:id=\"@android:id/list\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"0dp\"\n\t\t\tandroid:layout_weight=\"1\" />\n\t\t-->\n\n\t\t<EditText\n\t\t\tandroid:id=\"@+id/tempPassword1\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_margin=\"2dp\"\n\t\t\tandroid:hint=\"@string/enter_new_password\"\n\t\t\tandroid:inputType=\"textPassword\"\n\t\t\tandroid:importantForAutofill=\"no\"\n\t\t\tandroid:singleLine=\"true\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<EditText\n\t\t\tandroid:id=\"@+id/tempPassword2\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_margin=\"2dp\"\n\t\t\tandroid:hint=\"@string/confirm_new_password\"\n\t\t\tandroid:inputType=\"textPassword\"\n\t\t\tandroid:singleLine=\"true\"\n\t\t\tandroid:importantForAutofill=\"no\"\n\t\t\tandroid:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"/>\n\n\t\t<View\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"1dp\"\n\t\t\tandroid:layout_marginVertical=\"10dp\"\n\t\t\tandroid:background=\"?android:attr/dividerHorizontal\"/>\n\n\t\t<Button\n\t\t\tandroid:id=\"@+id/applyPassword\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_gravity=\"center_horizontal\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:text=\"@string/apply\"/>\n\n\t\t<Button\n\t\t\tandroid:id=\"@+id/clearPassword\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_gravity=\"center_horizontal\"\n\t\t\tandroid:text=\"@string/clear_password\"/>\n\n\t</LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/dialog_ok_cancel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:id=\"@+id/button_container\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:orientation=\"vertical\">\n\n\t<!-- replicates the bottom row of an AlertDialog, with \"Cancel\" on the left\n\t and \"OK\" on the right. It uses the default style, and supports Android 13\n\t  user-picked accent colors for buttons -->\n\n\t<!-- TODO There should be an androidX class that builds this: remove and use that -->\n\n\t<View\n\t\tandroid:id=\"@+id/divider\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"1dp\"\n\t\tandroid:background=\"?android:attr/dividerHorizontal\"/>\n\n\t<LinearLayout\n\t\tstyle=\"?buttonBarStyle\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:orientation=\"horizontal\">\n\n\t\t<Button\n\t\t\tandroid:id=\"@+id/dialog_no\"\n\t\t\tstyle=\"?buttonBarButtonStyle\"\n\t\t\tandroid:layout_width=\"0dp\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_weight=\"1\"\n\t\t\tandroid:text=\"@android:string/cancel\"/>\n\n\t\t<Button\n\t\t\tandroid:id=\"@+id/dialog_yes\"\n\t\t\tstyle=\"?buttonBarButtonStyle\"\n\t\t\tandroid:layout_width=\"0dp\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_weight=\"1\"\n\t\t\tandroid:text=\"@android:string/ok\"/>\n\t</LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/drawer_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<!-- The header of a section in the drawer: as of now we have \"TASKS\" and then \"LISTS\" -->\n<TextView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:id=\"@android:id/text1\"\n\tstyle=\"?android:attr/listSeparatorTextViewStyle\"\n\tandroid:layout_width=\"fill_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:gravity=\"center_vertical\"\n\tandroid:paddingLeft=\"16dip\"\n\tandroid:paddingTop=\"16dip\"\n\tandroid:paddingBottom=\"2dip\"/>\n"
  },
  {
    "path": "app/src/main/res/layout/drawer_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!-- Sequence of clickable \"note list names\" which makes up\n the entirety of the drawer menu -->\n\n<!-- TODO it's kind of a hack. The recommended way is to have a\n      recyclerview with viewholders of different type, declared\n      in different classes, for each type of clickable item -->\n<ListView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:id=\"@+id/leftDrawer\"\n\tstyle=\"?attr/LeftDrawerStyle\"\n\tandroid:layout_width=\"320dp\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:layout_gravity=\"start\"\n\tandroid:choiceMode=\"none\"\n\ttools:listitem=\"@layout/simple_listitem\"/>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_dialog_editlist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<ScrollView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\">\n\n\t<LinearLayout\n\t\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:orientation=\"vertical\"\n\t\tandroid:paddingTop=\"16dp\">\n\n\t\t<EditText\n\t\t\tandroid:id=\"@+id/titleField\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginBottom=\"20dp\"\n\t\t\tandroid:fontFamily=\"sans-serif\"\n\t\t\tandroid:hint=\"@string/editor_title_hint\"\n\t\t\tandroid:importantForAutofill=\"no\"\n\t\t\tandroid:inputType=\"textCapSentences\"\n\t\t\tandroid:singleLine=\"true\"\n\t\t\tandroid:textAppearance=\"@android:style/TextAppearance.Large\"\n\t\t\tandroid:textStyle=\"normal\"/>\n\n\t\t<CheckBox\n\t\t\tandroid:id=\"@+id/defaultListBox\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginBottom=\"16dp\"\n\t\t\tandroid:fontFamily=\"sans-serif\"\n\t\t\tandroid:text=\"@string/menu_setdefaultlist\"\n\t\t\tandroid:textAppearance=\"@android:style/TextAppearance.Medium\"\n\t\t\tandroid:textColor=\"?android:attr/textColorSecondary\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:paddingHorizontal=\"16dp\"\n\t\t\tandroid:text=\"@string/order_notes_by\"/>\n\n\t\t<Spinner\n\t\t\tandroid:id=\"@+id/sortSpinner\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"48dp\"\n\t\t\tandroid:spinnerMode=\"dropdown\"\n\t\t\ttools:listitem=\"@layout/spinner_item\"/>\n\n\t\t<TextView\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:paddingHorizontal=\"16dp\"\n\t\t\tandroid:text=\"@string/show_elements_as\"/>\n\n\t\t<Spinner\n\t\t\tandroid:id=\"@+id/modeSpinner\"\n\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\tandroid:layout_height=\"48dp\"\n\t\t\tandroid:spinnerMode=\"dropdown\"\n\t\t\ttools:listitem=\"@layout/spinner_item\"/>\n\n\t\t<TextView\n\t\t\tandroid:id=\"@+id/deleteButton\"\n\t\t\tstyle=\"?android:attr/buttonBarButtonStyle\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\tandroid:layout_marginBottom=\"4dp\"\n\t\t\tandroid:clickable=\"true\"\n\t\t\tandroid:drawablePadding=\"8dp\"\n\t\t\tandroid:focusable=\"true\"\n\t\t\tandroid:fontFamily=\"sans-serif\"\n\t\t\tandroid:gravity=\"center_vertical\"\n\t\t\tandroid:paddingHorizontal=\"16dp\"\n\t\t\tandroid:text=\"@string/menu_deletelist\"\n\t\t\tandroid:textAllCaps=\"true\"\n\t\t\tandroid:textAppearance=\"@android:style/TextAppearance.Medium\"\n\t\t\tandroid:textColor=\"?android:attr/textColorSecondary\"\n\t\t\tandroid:textStyle=\"bold\"\n\t\t\tapp:drawableLeftCompat=\"@drawable/ic_delete_24dp\"\n\t\t\tapp:drawableTint=\"@color/material_red\"/>\n\n\t\t<include\n\t\t\tandroid:id=\"@+id/buttons\"\n\t\t\tlayout=\"@layout/dialog_ok_cancel\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_marginTop=\"16dp\"/>\n\n\t</LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/fragment_dialog_movetolist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:orientation=\"vertical\">\n\n\t<ListView\n\t\tandroid:id=\"@+id/listView\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:choiceMode=\"singleChoice\"\n\t\tandroid:divider=\"?android:attr/listDivider\"\n\t\tandroid:dividerHeight=\"1px\"\n\n\t\ttools:listitem=\"@layout/simple_listitem\"/>\n\n\t<include\n\t\tandroid:id=\"@+id/buttons\"\n\t\tlayout=\"@layout/dialog_ok_cancel\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginTop=\"16dp\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_dialog_password.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:orientation=\"vertical\">\n\n\t<EditText\n\t\tandroid:id=\"@+id/passwordField\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"48dp\"\n\t\tandroid:hint=\"@string/enter_password\"\n\t\tandroid:inputType=\"textPassword\"\n\t\tandroid:paddingLeft=\"16dp\"\n\t\tandroid:paddingTop=\"8dp\"\n\t\tandroid:paddingRight=\"16dp\"\n\t\tandroid:paddingBottom=\"8dp\"\n\t\tandroid:singleLine=\"true\"\n\t\tandroid:importantForAutofill=\"no\">\n\n\t\t<requestFocus/>\n\t</EditText>\n\n\t<EditText\n\t\tandroid:id=\"@+id/passwordVerificationField\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"48dp\"\n\t\tandroid:hint=\"@string/confirm_new_password\"\n\t\tandroid:inputType=\"textPassword\"\n\t\tandroid:paddingLeft=\"16dp\"\n\t\tandroid:paddingTop=\"8dp\"\n\t\tandroid:paddingRight=\"16dp\"\n\t\tandroid:paddingBottom=\"8dp\"\n\t\tandroid:singleLine=\"true\"\n\t\tandroid:visibility=\"gone\"\n\t\tandroid:importantForAutofill=\"no\"/>\n\n\t<include\n\t\tandroid:id=\"@+id/buttons\"\n\t\tlayout=\"@layout/dialog_ok_cancel\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_dialog_restore.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n\n<!-- the popup that comes up if you restore a deleted task from the history page -->\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:orientation=\"vertical\"\n\tandroid:paddingTop=\"16dp\">\n\n\t<Spinner\n\t\tandroid:id=\"@+id/listSpinner\"\n\t\tandroid:layout_width=\"fill_parent\"\n\t\tandroid:layout_height=\"48dp\"\n\t\tandroid:layout_marginTop=\"4dp\"\n\t\tandroid:layout_marginBottom=\"16dp\"\n\t\tandroid:spinnerMode=\"dropdown\"\n\n\t\ttools:listitem=\"@layout/spinner_item\"/>\n\n\t<include\n\t\tandroid:id=\"@+id/buttons\"\n\t\tlayout=\"@layout/dialog_ok_cancel\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginTop=\"16dp\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tstyle=\"?attr/ListMarginStyle\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:orientation=\"vertical\"\n\tandroid:fitsSystemWindows=\"true\">\n\n\t<!-- Layout file for the \"archive\" activity of deleted notes. Shows a list of those.\n\t See FragmentSearchDeleted, which decides what gets shown in the listitem -->\n\n\t<ListView\n\t\tandroid:id=\"@android:id/list\"\n\t\tstyle=\"?attr/ListDividerStyle\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_marginVertical=\"4dp\"\n\t\tandroid:drawSelectorOnTop=\"true\"\n\t\ttools:listitem=\"@layout/tasklist_item_rich\">\n\t\t<!-- every height is \"match_parent\" because the ListView can show the scrollbar by itself -->\n\t</ListView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_task_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<ScrollView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:id=\"@+id/editScrollView\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:layout_marginLeft=\"@dimen/editor_side_margin\"\n\tandroid:layout_marginRight=\"@dimen/editor_side_margin\"\n\tandroid:fillViewport=\"@bool/fillEditor\">\n\n\t<LinearLayout\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:orientation=\"vertical\">\n\n\t\t<RelativeLayout\n\t\t\tstyle=\"@style/EditorLayoutStyle\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"0dp\"\n\t\t\tandroid:layout_weight=\"1\"\n\t\t\ttools:context=\".TaskDetailFragment\">\n\n\t\t\t<com.nononsenseapps.ui.StyledEditText\n\t\t\t\tandroid:id=\"@+id/taskText\"\n\t\t\t\tstyle=\"@style/EditorTextStyle\"\n\t\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_alignParentTop=\"true\"\n\t\t\t\tandroid:layout_marginTop=\"16dp\"\n\t\t\t\tandroid:gravity=\"top\"\n\t\t\t\tandroid:hint=\"@string/editor_note_hint\"\n\t\t\t\tandroid:inputType=\"textCapSentences|textAutoCorrect|textMultiLine\"\n\t\t\t\tandroid:minHeight=\"100dp\"\n\t\t\t\tandroid:nextFocusDown=\"@+id/taskCompleted\"\n\t\t\t\tapp:linkify=\"true\"\n\t\t\t\tapp:titleFontFamily=\"robotocondensed\"\n\t\t\t\tapp:titleFontStyle=\"bold\"\n\t\t\t\tapp:titleRelativeSize=\"1.3\"/>\n\n\t\t\t<!-- DUE DATE SECTION -->\n\n\t\t\t<RelativeLayout\n\t\t\t\tandroid:id=\"@+id/taskSection\"\n\t\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_below=\"@+id/taskText\"\n\t\t\t\tandroid:layout_marginTop=\"16dp\">\n\n\t\t\t\t<com.nononsenseapps.ui.DelegateFrame\n\t\t\t\t\txmlns:nononsenseapps=\"http://nononsenseapps.com\"\n\t\t\t\t\tandroid:id=\"@+id/checkboxcontainer\"\n\t\t\t\t\tandroid:layout_width=\"48dp\"\n\t\t\t\t\tandroid:layout_height=\"48dp\"\n\t\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\t\tandroid:layout_alignParentTop=\"true\"\n\t\t\t\t\tandroid:clickable=\"true\"\n\t\t\t\t\tnononsenseapps:enlargedView=\"@+id/taskCompleted\">\n\n\t\t\t\t\t<CheckBox\n\t\t\t\t\t\tandroid:id=\"@+id/taskCompleted\"\n\t\t\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\t\t\tandroid:layout_height=\"48dp\"\n\t\t\t\t\t\tandroid:layout_centerInParent=\"true\"\n\t\t\t\t\t\tandroid:gravity=\"center\"\n\t\t\t\t\t\tandroid:nextFocusRight=\"@+id/dueDateBox\"\n\t\t\t\t\t\tandroid:nextFocusDown=\"@+id/notificationAdd\"/>\n\t\t\t\t</com.nononsenseapps.ui.DelegateFrame>\n\n\t\t\t\t<ImageButton\n\t\t\t\t\tandroid:id=\"@+id/dueCancelButton\"\n\t\t\t\t\tstyle=\"?android:attr/buttonBarButtonStyle\"\n\t\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\t\tandroid:layout_height=\"48dp\"\n\t\t\t\t\tandroid:layout_alignParentTop=\"true\"\n\t\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\t\tandroid:contentDescription=\"Remove due date\"\n\t\t\t\t\tandroid:nextFocusDown=\"@+id/notificationAdd\"\n\t\t\t\t\tandroid:src=\"@drawable/ic_clear_24dp\"\n\t\t\t\t\ttools:ignore=\"HardcodedText\"/>\n\n\t\t\t\t<!-- This shows the due date. Clicking it opens a datepicker.\n\t\t\t\t The spinner style here looks better than the default button style,\n\t\t\t\t but you may want to choose something else. Just try -->\n\t\t\t\t<Button\n\t\t\t\t\tandroid:id=\"@+id/dueDateBox\"\n\t\t\t\t\tstyle=\"?android:attr/spinnerStyle\"\n\t\t\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\t\tandroid:layout_alignParentTop=\"true\"\n\t\t\t\t\tandroid:layout_toLeftOf=\"@+id/dueCancelButton\"\n\t\t\t\t\tandroid:layout_toRightOf=\"@+id/checkboxcontainer\"\n\t\t\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\t\t\tandroid:gravity=\"center_vertical|left\"\n\t\t\t\t\tandroid:hint=\"@string/editor_due_date_hint\"\n\t\t\t\t\tandroid:minHeight=\"48dp\"\n\t\t\t\t\tandroid:nextFocusRight=\"@+id/dueCancelButton\"\n\t\t\t\t\tandroid:nextFocusDown=\"@+id/notificationAdd\"\n\t\t\t\t\tandroid:paddingLeft=\"16dp\"\n\t\t\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"/>\n\t\t\t</RelativeLayout>\n\n\t\t\t<TextView\n\t\t\t\tandroid:id=\"@+id/notificationAdd\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"48dp\"\n\t\t\t\tandroid:layout_alignWithParentIfMissing=\"true\"\n\t\t\t\tandroid:layout_below=\"@id/taskSection\"\n\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\tandroid:layout_marginTop=\"8dp\"\n\t\t\t\tandroid:layout_marginBottom=\"0dp\"\n\t\t\t\tandroid:background=\"?android:attr/selectableItemBackground\"\n\t\t\t\tandroid:clickable=\"true\"\n\t\t\t\tandroid:focusable=\"true\"\n\t\t\t\tandroid:drawablePadding=\"8dp\"\n\t\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\t\tandroid:gravity=\"center_vertical\"\n\t\t\t\tandroid:nextFocusDown=\"@+id/notificationList\"\n\t\t\t\tandroid:paddingRight=\"8dp\"\n\t\t\t\tandroid:text=\"@string/add_a_reminder\"\n\t\t\t\tandroid:textAllCaps=\"false\"\n\t\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\t\t\tandroid:textColor=\"?android:attr/textColorSecondary\"\n\t\t\t\tapp:drawableLeftCompat=\"@drawable/ic_alarm_add_24dp\"/>\n\n\t\t\t<LinearLayout\n\t\t\t\tandroid:id=\"@+id/notificationList\"\n\t\t\t\tandroid:layout_width=\"fill_parent\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_below=\"@+id/notificationAdd\"\n\t\t\t\tandroid:layout_marginTop=\"4dp\"\n\t\t\t\tandroid:layout_marginBottom=\"0dp\"\n\t\t\t\tandroid:animateLayoutChanges=\"true\"\n\t\t\t\tandroid:orientation=\"vertical\"\n\t\t\t\tandroid:paddingLeft=\"0dp\"\n\t\t\t\tandroid:paddingBottom=\"8dp\">\n\t\t\t</LinearLayout>\n\t\t</RelativeLayout>\n\n\t\t<android.widget.Space\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"@dimen/editor_vertical_margin\"/>\n\t</LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/fragment_task_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<FrameLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\tstyle=\"?attr/ListMarginStyle\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\">\n\n\t<LinearLayout\n\t\tandroid:id=\"@+id/hintContainer\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:gravity=\"center\"\n\t\tandroid:orientation=\"vertical\"\n\t\tandroid:visibility=\"gone\">\n\n\t\t<TextView\n\t\t\tstyle=\"@style/FragmentHint\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:text=\"@string/please_create_note\"/>\n\t</LinearLayout>\n\n\t<FrameLayout\n\t\tandroid:id=\"@+id/listContainer\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:visibility=\"visible\">\n\n\t\t<androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n\t\t\tandroid:id=\"@+id/ptrLayout\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"match_parent\">\n\n\t\t\t<com.mobeta.android.dslv.DragSortListView\n\t\t\t\tandroid:id=\"@android:id/list\"\n\t\t\t\tstyle=\"?attr/ListDividerStyle\"\n\t\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\t\tandroid:layout_height=\"match_parent\"\n\t\t\t\tandroid:drawSelectorOnTop=\"true\"\n\t\t\t\tapp:collapsed_height=\"1px\"\n\t\t\t\tapp:drag_enabled=\"true\"\n\t\t\t\tapp:drag_handle_id=\"@+id/drag_handle\"\n\t\t\t\tapp:drag_scroll_start=\"0.33\"\n\t\t\t\tapp:drag_start_mode=\"onMove\"\n\t\t\t\tapp:float_alpha=\"1.0\"\n\t\t\t\tapp:float_background_color=\"@android:color/holo_blue_dark\"\n\t\t\t\tapp:remove_enabled=\"false\"\n\t\t\t\tapp:remove_mode=\"flingRemove\"\n\t\t\t\tapp:slide_shuffle_speed=\"0.3\"\n\t\t\t\tapp:sort_enabled=\"true\"/>\n\n\t\t</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n\t</FrameLayout>\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_tasklist_viewpager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.viewpager.widget.ViewPager\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:id=\"@+id/pager\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\">\n\n\t<!--\n\tThis title strip will display the currently visible page title, as well as the page\n\ttitles for adjacent pages.\n\t-->\n\n\t<androidx.viewpager.widget.PagerTitleStrip\n\t\tandroid:id=\"@+id/pager_title_strip\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_gravity=\"top\"\n\t\tandroid:background=\"?attr/viewpager_bg\"\n\t\tandroid:paddingTop=\"4dp\"\n\t\tandroid:paddingBottom=\"4dp\"\n\t\tandroid:textColor=\"?android:attr/textColorSecondary\"/>\n\n</androidx.viewpager.widget.ViewPager>\n"
  },
  {
    "path": "app/src/main/res/layout/fullscreen_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:id=\"@+id/fragmentPlaceHolder\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"/>"
  },
  {
    "path": "app/src/main/res/layout/notification_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- a 'reminder' item, with the clock icon, the due date, the due time\n and 7 buttons for the week days -->\n<RelativeLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:paddingTop=\"2dp\"\n\tandroid:paddingBottom=\"2dp\">\n\n\t<ImageButton\n\t\tandroid:id=\"@+id/notificationRemove\"\n\t\tstyle=\"?android:attr/buttonBarButtonStyle\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"48dp\"\n\t\tandroid:layout_alignParentRight=\"true\"\n\t\tandroid:layout_marginLeft=\"4dp\"\n\t\tandroid:contentDescription=\"Remove reminder\"\n\t\tandroid:scaleType=\"center\"\n\t\tandroid:src=\"@drawable/ic_clear_24dp\"\n\t\ttools:ignore=\"HardcodedText\"/>\n\n\t<ImageButton\n\t\tandroid:id=\"@+id/notificationTypeTime\"\n\t\tstyle=\"?android:attr/buttonBarButtonStyle\"\n\t\tandroid:layout_width=\"40dp\"\n\t\tandroid:layout_height=\"48dp\"\n\t\tandroid:layout_alignParentLeft=\"true\"\n\t\tandroid:background=\"?android:attr/selectableItemBackground\"\n\t\tandroid:contentDescription=\"Type of notification\"\n\t\tandroid:minWidth=\"8dp\"\n\t\tandroid:padding=\"0dp\"\n\t\tandroid:scaleType=\"center\"\n\t\tandroid:src=\"@drawable/ic_alarm_24dp\"\n\t\ttools:ignore=\"HardcodedText\"/>\n\n\t<TextView\n\t\tandroid:id=\"@+id/notificationDate\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginLeft=\"4dp\"\n\t\tandroid:layout_marginRight=\"4dp\"\n\t\tandroid:layout_toRightOf=\"@id/notificationTypeTime\"\n\t\tandroid:background=\"?android:attr/selectableItemBackground\"\n\t\tandroid:clickable=\"true\"\n\t\tandroid:focusable=\"true\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:gravity=\"center\"\n\t\tandroid:hint=\"@string/time\"\n\t\tandroid:minWidth=\"48dp\"\n\t\tandroid:minHeight=\"48dp\"\n\t\tandroid:singleLine=\"true\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\tandroid:textIsSelectable=\"false\"/>\n\n\t<TextView\n\t\tandroid:id=\"@+id/notificationTime\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_marginLeft=\"4dp\"\n\t\tandroid:layout_marginRight=\"4dp\"\n\t\tandroid:layout_toRightOf=\"@id/notificationDate\"\n\t\tandroid:background=\"?android:attr/selectableItemBackground\"\n\t\tandroid:clickable=\"true\"\n\t\tandroid:focusable=\"true\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:gravity=\"center\"\n\t\tandroid:hint=\"@string/time\"\n\t\tandroid:minWidth=\"48dp\"\n\t\tandroid:minHeight=\"48dp\"\n\t\tandroid:singleLine=\"true\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\tandroid:textIsSelectable=\"false\"/>\n\n\t<com.nononsenseapps.ui.WeekDaysView\n\t\tandroid:id=\"@+id/weekdays\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"48dp\"\n\t\tandroid:layout_below=\"@id/notificationTypeTime\"\n\t\tandroid:layout_alignParentLeft=\"true\"\n\t\tandroid:paddingLeft=\"8dp\"\n\t\tandroid:paddingRight=\"16dp\"/>\n\n\t<CheckBox\n\t\tandroid:id=\"@+id/repeatSwitch\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"48dp\"\n\t\tandroid:layout_below=\"@id/weekdays\"\n\t\tandroid:layout_alignParentLeft=\"true\"\n\t\tandroid:layout_marginLeft=\"8dp\"\n\t\tandroid:layout_toLeftOf=\"@id/notificationRemove\"\n\t\tandroid:text=\"@string/repeat\"\n\t\tandroid:visibility=\"gone\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/simple_light_list_item_activated_1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:id=\"@android:id/text1\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"64dp\"\n\tandroid:background=\"?android:attr/activatedBackgroundIndicator\"\n\tandroid:fontFamily=\"sans-serif-light\"\n\tandroid:gravity=\"center_vertical\"\n\tandroid:minHeight=\"?android:attr/listPreferredItemHeightLarge\"\n\tandroid:paddingLeft=\"16dp\"\n\tandroid:paddingRight=\"16dp\"\n\tandroid:textAppearance=\"?android:attr/textAppearanceListItem\"\n\tandroid:textColor=\"?android:attr/textColorPrimary\"/>\n"
  },
  {
    "path": "app/src/main/res/layout/simple_listitem.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<!-- This represents a \"list of notes\" shown in the drawer menu on the left.\n See also drawer_layout.xml which hosts these list items.\n This item will enlarge to show all the text of the notelist name -->\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:background=\"?android:attr/selectableItemBackground\"\n\tandroid:orientation=\"horizontal\"\n\tandroid:paddingVertical=\"8dp\">\n\n\t<!-- shows the list's name. Uses as many lines as the note-list needs,\n\tor else, on devices with bigger text size setting, the name would be cut -->\n\t<TextView\n\t\tandroid:drawablePadding=\"16dp\"\n\t\tandroid:id=\"@android:id/text1\"\n\t\tandroid:background=\"?android:attr/selectableItemBackground\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:paddingHorizontal=\"8dp\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_gravity=\"start|center_vertical\"\n\t\tandroid:layout_weight=\"1\"\n\t\ttools:text=\"note list name note list name note list name note list name \"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:textColor=\"?android:attr/textColorPrimary\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\tapp:drawableLeftCompat=\"@drawable/folder_move_24dp\"/>\n\n\t<!-- Shows the number of notes in this list, on the right -->\n\t<TextView\n\t\tandroid:id=\"@android:id/text2\"\n\t\tandroid:paddingHorizontal=\"16dp\"\n\t\tandroid:background=\"?android:attr/selectableItemBackground\"\n\t\tandroid:textColor=\"?android:attr/textColorSecondary\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:gravity=\"center_vertical\"\n\t\tandroid:layout_gravity=\"end|center_vertical\"\n\t\tandroid:ellipsize=\"end\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:maxLines=\"1\"\n\t\ttools:text=\"12\"\n\t\tandroid:singleLine=\"true\"/>\n\n\t<!-- TODO here you should add a button which, once pressed, will show the popup to edit\n\t      the notelist. Remember that this layout is used both for the first 3 items\n\t      (Overdue, Today, Next 5 days) and for the notelists created by the user -->\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/spinner_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- the style of items shown, for example, in the spinner of the popup\n where you choose how to order notes in a list -->\n<TextView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:id=\"@+id/textViewSpinnerItem\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:fontFamily=\"sans-serif\"\n\tandroid:gravity=\"center_vertical\"\n\tandroid:minHeight=\"48dp\"\n\tandroid:paddingHorizontal=\"16dp\"\n\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\tandroid:textColor=\"?android:attr/textColorSecondary\"\n\ttools:ignore=\"SelectableText\"\n\tandroid:singleLine=\"true\"\n\tandroid:ellipsize=\"marquee\"/>"
  },
  {
    "path": "app/src/main/res/layout/tasklist_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:custom=\"http://schemas.android.com/apk/res-auto\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:clickable=\"false\"\n\tandroid:orientation=\"horizontal\">\n\n\t<!-- Keeping as this class for compatibility with other layout -->\n\n\t<com.nononsenseapps.ui.TitleNoteTextView\n\t\tandroid:id=\"@android:id/text1\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:ellipsize=\"end\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:paddingLeft=\"16dp\"\n\t\tandroid:singleLine=\"true\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceSmall\"\n\t\tandroid:textColor=\"?android:attr/textColorPrimary\"\n\t\tandroid:textIsSelectable=\"false\"\n\t\tcustom:titleFontFamily=\"robotolight\"/>\n\n\t<!-- Needed to support the same cursor as items . All hidden -->\n\n\t<TextView\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:visibility=\"gone\"/>\n\n\t<com.nononsenseapps.ui.NoteCheckBox\n\t\tandroid:id=\"@+id/checkbox\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:visibility=\"gone\"/>\n\n\t<com.nononsenseapps.ui.DateView\n\t\tandroid:id=\"@+id/date\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:visibility=\"gone\"/>\n\n\t<View\n\t\tandroid:id=\"@+id/dragpadding\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:visibility=\"gone\"/>\n\n\t<View\n\t\tandroid:id=\"@+id/drag_handle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:visibility=\"gone\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/tasklist_item_card_section.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- This is a \"list item\" representing a note in the drag-sort-listview -->\n<RelativeLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:minHeight=\"@dimen/list_item_min_size\"\n\tandroid:paddingLeft=\"4dp\"\n\tandroid:paddingTop=\"4dp\"\n\tandroid:paddingRight=\"8dp\"\n\tandroid:paddingBottom=\"4dp\">\n\n\t<!-- if you add \"focusable, it doesn't let you click the note -->\n\t<com.nononsenseapps.ui.DelegateFrame\n\t\tandroid:id=\"@+id/checkboxcontainer\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_centerVertical=\"true\"\n\t\tandroid:clickable=\"true\"\n\t\tandroid:minHeight=\"40dp\"\n\t\tandroid:paddingHorizontal=\"4dp\"\n\t\tapp:enlargedView=\"@+id/itemDone\"\n\t\tandroid:focusable=\"false\"\n\t\ttools:ignore=\"KeyboardInaccessibleWidget\">\n\n\t\t<com.nononsenseapps.ui.NoteCheckBox\n\t\t\tandroid:id=\"@+id/checkbox\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\tandroid:focusable=\"false\"\n\t\t\tandroid:focusableInTouchMode=\"false\"\n\t\t\tandroid:text=\"\"/>\n\t</com.nononsenseapps.ui.DelegateFrame>\n\n\t<!-- ensures that the date view on the bottom does not overlap with the squares that\n\tyou hold to drag the note -->\n\t<View\n\t\tandroid:id=\"@+id/dragpadding\"\n\t\tandroid:layout_width=\"@dimen/drag_grip_avoid_padding\"\n\t\tandroid:layout_height=\"1dp\"\n\t\tandroid:layout_alignParentRight=\"true\"/>\n\n\t<com.nononsenseapps.ui.TitleNoteTextView\n\t\tandroid:id=\"@android:id/text1\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_alignWithParentIfMissing=\"true\"\n\t\tandroid:layout_alignBaseline=\"@+id/checkboxcontainer\"\n\t\tandroid:layout_centerVertical=\"true\"\n\t\tandroid:layout_toLeftOf=\"@+id/dragpadding\"\n\t\tandroid:layout_toRightOf=\"@+id/checkboxcontainer\"\n\t\tandroid:ellipsize=\"end\"\n\t\tandroid:maxLines=\"4\"\n\t\tandroid:paddingBottom=\"4dp\"\n\t\tandroid:singleLine=\"false\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceSmall\"\n\t\tandroid:textColor=\"?android:attr/textColorPrimary\"\n\t\tandroid:textIsSelectable=\"false\"\n\t\tapp:linkify=\"true\"\n\t\tapp:secondaryColor=\"@color/completedGrey\"\n\t\tapp:titleFontFamily=\"robotocondensed\"\n\t\tapp:titleFontStyle=\"bold\"\n\t\tapp:titleRelativeSize=\"1.2\"/>\n\n\t<com.nononsenseapps.ui.DateView\n\t\tandroid:id=\"@+id/date\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_below=\"@android:id/text1\"\n\t\tandroid:layout_toLeftOf=\"@+id/dragpadding\"\n\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\tandroid:gravity=\"right\"\n\t\tandroid:paddingTop=\"10dp\"\n\t\tandroid:textColor=\"?android:attr/textColorSecondary\"\n\t\tandroid:textSize=\"12sp\"\n\t\tandroid:visibility=\"gone\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/tasklist_item_rich.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<FrameLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tstyle=\"?attr/TaskListItem\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:minHeight=\"@dimen/list_item_min_size\">\n\n\t<include\n\t\tlayout=\"@layout/tasklist_item_card_section\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"/>\n\n\t<com.google.android.apps.dashclock.ui.DragGripView\n\t\tandroid:id=\"@+id/drag_handle\"\n\t\tandroid:layout_width=\"@dimen/drag_grip_width\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_gravity=\"fill_vertical|end\"\n\t\tandroid:color=\"?attr/drag_handle_color\"\n\t\tandroid:minHeight=\"@dimen/list_item_min_size\"\n\t\tandroid:paddingTop=\"12dp\"\n\t\tandroid:paddingRight=\"4dp\"\n\t\tandroid:paddingBottom=\"12dp\"/>\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/weekdays_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"36dp\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\ttools:ignore=\"HardcodedText\"\n\tandroid:orientation=\"horizontal\">\n\n\t<com.nononsenseapps.ui.GreyableToggleButton\n\t\tandroid:id=\"@+id/day1\"\n\t\tstyle=\"@style/GreyableButtonToggle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:textOff=\"WED\"/>\n\n\t<com.nononsenseapps.ui.GreyableToggleButton\n\t\tandroid:id=\"@+id/day2\"\n\t\tstyle=\"@style/GreyableButtonToggle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:textOff=\"WED\"/>\n\n\t<com.nononsenseapps.ui.GreyableToggleButton\n\t\tandroid:id=\"@+id/day3\"\n\t\tstyle=\"@style/GreyableButtonToggle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:textOff=\"WED\"/>\n\n\t<com.nononsenseapps.ui.GreyableToggleButton\n\t\tandroid:id=\"@+id/day4\"\n\t\tstyle=\"@style/GreyableButtonToggle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:textOff=\"WED\"/>\n\n\t<com.nononsenseapps.ui.GreyableToggleButton\n\t\tandroid:id=\"@+id/day5\"\n\t\tstyle=\"@style/GreyableButtonToggle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:textOff=\"WED\"/>\n\n\t<com.nononsenseapps.ui.GreyableToggleButton\n\t\tandroid:id=\"@+id/day6\"\n\t\tstyle=\"@style/GreyableButtonToggle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:textOff=\"WED\"/>\n\n\t<com.nononsenseapps.ui.GreyableToggleButton\n\t\tandroid:id=\"@+id/day7\"\n\t\tstyle=\"@style/GreyableButtonToggle\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:textOff=\"WED\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/widget_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:layout_margin=\"@dimen/widget_margin\"\n\tandroid:orientation=\"vertical\"\n\ttools:ignore=\"HardcodedText\">\n\n\t<LinearLayout\n\t\tandroid:id=\"@+id/widgetHeader\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:background=\"@color/googlenow_darkergrey_trans\"\n\t\tandroid:orientation=\"horizontal\"\n\t\tandroid:padding=\"0dp\">\n\n\t\t<ImageView\n\t\t\tandroid:id=\"@+id/widgetConfigButton\"\n\t\t\tandroid:layout_width=\"48dp\"\n\t\t\tandroid:layout_height=\"48dp\"\n\t\t\tandroid:layout_gravity=\"center\"\n\t\t\tandroid:background=\"@drawable/img_default_selector_dark\"\n\t\t\tandroid:contentDescription=\"Configure widget\"\n\t\t\tandroid:padding=\"8dp\"\n\t\t\tandroid:src=\"@drawable/ic_settings_24dp\"/>\n\n\t\t<TextView\n\t\t\tandroid:id=\"@+id/titleButton\"\n\t\t\tandroid:layout_width=\"0dp\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_gravity=\"center_vertical\"\n\t\t\tandroid:layout_weight=\"1\"\n\t\t\tandroid:background=\"@drawable/img_default_selector_dark\"\n\t\t\tandroid:gravity=\"center_vertical\"\n\t\t\tandroid:maxLines=\"2\"\n\t\t\tandroid:paddingLeft=\"0dp\"\n\t\t\tandroid:paddingTop=\"4dp\"\n\t\t\tandroid:paddingRight=\"4dp\"\n\t\t\tandroid:paddingBottom=\"4dp\"\n\t\t\tandroid:text=\"@string/loading_widget\"\n\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\t\tandroid:textColor=\"@android:color/primary_text_dark\"/>\n\n\t\t<ImageButton\n\t\t\tandroid:id=\"@+id/createNoteButton\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"match_parent\"\n\t\t\tandroid:background=\"@drawable/img_default_selector_dark\"\n\t\t\tandroid:contentDescription=\"Create new note\"\n\t\t\tandroid:padding=\"4dp\"\n\t\t\tandroid:scaleType=\"fitCenter\"\n\t\t\tandroid:src=\"@drawable/ic_add_24dp\"/>\n\t</LinearLayout>\n\n\t<FrameLayout\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"0dp\"\n\t\tandroid:layout_weight=\"1\"\n\t\tandroid:padding=\"0dp\">\n\n\t\t<ImageView\n\t\t\tandroid:id=\"@+id/shade\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"match_parent\"\n\t\t\tandroid:contentDescription=\"BackgroundColor\"\n\t\t\tandroid:visibility=\"gone\"/>\n\n\t\t<ListView\n\t\t\tandroid:id=\"@+id/notesList\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"match_parent\"\n\t\t\tandroid:divider=\"@null\"\n\t\t\tandroid:dividerHeight=\"1px\"\n\t\t\ttools:listitem=\"@layout/widgetlist_item\"/>\n\n\t\t<TextView\n\t\t\tandroid:id=\"@+id/empty_view\"\n\t\t\tstyle=\"@style/FragmentHint\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"match_parent\"\n\t\t\tandroid:gravity=\"center\"\n\t\t\tandroid:visibility=\"gone\"/>\n\t</FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/widgetlist_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<!-- this shows a section title like \"today\" \"tomorrow\" or \"later\" for the big widget,\nwhen notes are ordered by due date. If you write this layout in an incompatible way (android\nwidgets are picky about it), then an OS-provided view showing \"Loading...\" (or \"Caricamento...\"\nor whatever) will appear instead. This layout is similar to android.R.layout.simple_list_item_1,\nso you can use one of those in ListWidgetService.ListRemoteViewsFactory.getViewAt()\nbut I think that having it here lets us create a prettier style.\nAs of now it's aligned in the center, not on the left. That's just personal preference.\n -->\n<TextView\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:id=\"@android:id/text1\"\n\tandroid:layout_width=\"wrap_content\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:fontFamily=\"sans-serif-light\"\n\tandroid:paddingHorizontal=\"16dp\"\n\tandroid:paddingTop=\"8dp\"\n\tandroid:paddingBottom=\"4dp\"\n\tandroid:singleLine=\"true\"\n\ttools:text=\"TODAY\"\n\tandroid:textAppearance=\"@style/WidgetItemHeaderStyleDark\"/>\n"
  },
  {
    "path": "app/src/main/res/layout/widgetlist_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<FrameLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:minHeight=\"@dimen/widget_item_min_size\"\n\tandroid:orientation=\"horizontal\"\n\tandroid:paddingRight=\"12dp\"\n\ttools:ignore=\"HardcodedText\">\n\n\t<RelativeLayout\n\t\tandroid:id=\"@+id/widget_item\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_gravity=\"center_vertical|left\"\n\t\tandroid:background=\"@drawable/img_default_selector_dark\"\n\t\tandroid:gravity=\"center_vertical\"\n\t\tandroid:paddingLeft=\"16dp\">\n\n\t\t<LinearLayout\n\t\t\tandroid:id=\"@+id/itemSpacer\"\n\t\t\tandroid:layout_width=\"20dp\"\n\t\t\tandroid:layout_height=\"1dp\"\n\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\tandroid:orientation=\"vertical\"\n\t\t\tandroid:visibility=\"visible\"/>\n\n\t\t<TextView\n\t\t\tandroid:id=\"@android:id/text1\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_alignWithParentIfMissing=\"true\"\n\t\t\tandroid:layout_alignParentTop=\"true\"\n\t\t\tandroid:layout_toLeftOf=\"@+id/dueDate\"\n\t\t\tandroid:layout_toRightOf=\"@id/itemSpacer\"\n\t\t\tandroid:gravity=\"center_vertical\"\n\t\t\tandroid:maxLines=\"2\"\n\t\t\tandroid:paddingTop=\"2dp\"\n\t\t\tandroid:paddingRight=\"4dp\"\n\t\t\tandroid:paddingBottom=\"2dp\"\n\t\t\tandroid:text=\"a title  see very plainly with a second line like so which should be too long ideally\"\n\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceSmall\"\n\t\t\tandroid:textColor=\"@android:color/primary_text_dark\"\n\t\t\tandroid:textStyle=\"normal\"/>\n\n\t\t<TextView\n\t\t\tandroid:id=\"@+id/dueDate\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\tandroid:gravity=\"center_vertical\"\n\t\t\tandroid:text=\"12 mar\"\n\t\t\tandroid:textColor=\"@android:color/primary_text_dark\"\n\t\t\tandroid:textSize=\"12sp\"/>\n\t</RelativeLayout>\n\n\t<!-- The following is the implementation of checkboxes. You can't use actual checkboxes\n\tas widgets (see https://developer.android.com/reference/android/widget/RemoteViews ) so\n\tyou have to use ImageButton instead. There are 2, to support theme switching.\n\tYou Can't change image src through RemoteViews -->\n\n\t<ImageButton\n\t\tandroid:id=\"@+id/completedCheckBoxDark\"\n\t\tandroid:layout_width=\"48dp\"\n\t\tandroid:layout_height=\"@dimen/widget_item_min_size\"\n\t\tandroid:layout_gravity=\"center_vertical|left\"\n\t\tandroid:background=\"@null\"\n\t\tandroid:contentDescription=\"Complete task\"\n\t\tandroid:padding=\"5dp\"\n\t\tandroid:scaleType=\"fitStart\"\n\t\tandroid:src=\"@drawable/ic_checkbox_unchecked\"/>\n\n\t<ImageButton\n\t\tandroid:id=\"@+id/completedCheckBoxLight\"\n\t\tandroid:layout_width=\"48dp\"\n\t\tandroid:layout_height=\"@dimen/widget_item_min_size\"\n\t\tandroid:layout_gravity=\"center_vertical|left\"\n\t\tandroid:background=\"@null\"\n\t\tandroid:contentDescription=\"Complete task\"\n\t\tandroid:padding=\"5dp\"\n\t\tandroid:scaleType=\"fitStart\"\n\t\tandroid:src=\"@drawable/ic_checkbox_unchecked\"\n\t\tandroid:visibility=\"gone\"/>\n\n\t<!-- TODO remove the 2° imagebutton. now they're basically clones with != names -->\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout-sw600dp-land/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<androidx.drawerlayout.widget.DrawerLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:id=\"@+id/drawerLayout\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:fitsSystemWindows=\"true\">\n\n\t<!-- The main content view -->\n\n\t<LinearLayout\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_marginLeft=\"16dp\"\n\t\tandroid:layout_marginRight=\"8dp\"\n\t\tandroid:animateLayoutChanges=\"true\"\n\t\tandroid:baselineAligned=\"false\"\n\t\tandroid:orientation=\"horizontal\"\n\t\tandroid:showDividers=\"middle\">\n\n\t\t<FrameLayout\n\t\t\tandroid:id=\"@+id/fragment1\"\n\t\t\tandroid:layout_width=\"0dp\"\n\t\t\tandroid:layout_height=\"match_parent\"\n\t\t\tandroid:layout_marginRight=\"4dp\"\n\t\t\tandroid:layout_weight=\"@integer/leftFragmentWeight\"/>\n\n\t\t<!-- Only present on tablets -->\n\t\t<FrameLayout\n\t\t\tandroid:id=\"@+id/fragment2\"\n\t\t\tandroid:layout_width=\"0dp\"\n\t\t\tandroid:layout_height=\"match_parent\"\n\t\t\tandroid:layout_marginLeft=\"4dp\"\n\t\t\tandroid:layout_weight=\"@integer/rightFragmentWeight\">\n\n\t\t\t<!-- Shown on tablets on start up. Hide on selection -->\n\t\t\t<TextView\n\t\t\t\tandroid:id=\"@+id/taskHint\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_gravity=\"center\"\n\t\t\t\tandroid:enabled=\"false\"\n\t\t\t\tandroid:fontFamily=\"sans-serif-light\"\n\t\t\t\tandroid:text=\"@string/please_select_note\"\n\t\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceLarge\"/>\n\t\t</FrameLayout>\n\t</LinearLayout>\n\n\t<!-- The navigation drawer -->\n\n\t<include\n\t\tandroid:id=\"@+id/leftDrawer\"\n\t\tlayout=\"@layout/drawer_layout\"\n\t\tandroid:layout_width=\"320dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_gravity=\"start\"/>\n\n</androidx.drawerlayout.widget.DrawerLayout>\n"
  },
  {
    "path": "app/src/main/res/layout-sw600dp-land/activity_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!-- Contains the \"main menu\" fragment for the settings activity.\n \t This version shows a dual pane, for tablets in landscape mode -->\n\n<LinearLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:orientation=\"horizontal\"\n\tandroid:fitsSystemWindows=\"true\"\n\tandroid:showDividers=\"middle\">\n\n\t<!-- this holds the fragment of the \"main menu\" -->\n\t<androidx.fragment.app.FragmentContainerView\n\t\tandroid:id=\"@+id/fragment\"\n\t\tandroid:name=\"com.nononsenseapps.notepad.prefs.IndexPrefs\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_marginRight=\"4dp\"\n\t\tandroid:layout_weight=\"@integer/leftFragmentWeight\"/>\n\n\t<!-- vertical separator -->\n\t<View\n\t\tandroid:layout_width=\"1dp\"\n\t\tandroid:layout_marginVertical=\"8dp\"\n\t\tandroid:layout_marginHorizontal=\"4dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:background=\"?android:attr/dividerHorizontal\"/>\n\n\t<!-- this holds the fragment with the selected preferences category -->\n\t<androidx.fragment.app.FragmentContainerView\n\t\tandroid:id=\"@+id/fragmentRightForTablets\"\n\t\tandroid:layout_width=\"0dp\"\n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_marginLeft=\"4dp\"\n\t\tandroid:layout_weight=\"@integer/rightFragmentWeight\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/menu/activity_deleted_context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- These 2 options are shown when you touch a note in the \"archive\" activity -->\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\t<item\n\t\tandroid:id=\"@+id/menu_restore\"\n\t\tandroid:title=\"@string/restore\"\n\t\tapp:showAsAction=\"always|withText\"/>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_select_all\"\n\t\tandroid:icon=\"@drawable/ic_select_all\"\n\t\tandroid:title=\"@string/select_all\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_delete\"\n\t\tandroid:icon=\"@drawable/ic_delete_24dp\"\n\t\tandroid:title=\"@string/menu_delete\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\t<group android:id=\"@+id/activity_menu_group\">\n\t\t<item\n\t\t\tandroid:id=\"@+id/menu_add\"\n\t\t\tandroid:icon=\"@drawable/ic_add_24dp\"\n\t\t\tandroid:orderInCategory=\"0\"\n\t\t\tandroid:title=\"@string/menu_save_and_add\"\n\t\t\tapp:showAsAction=\"ifRoom\"/>\n\t</group>\n\t<group\n\t\tandroid:id=\"@+id/activity_reverse_menu_group\"\n\t\tandroid:visible=\"false\">\n\t\t<item\n\t\t\tandroid:id=\"@+id/drawer_menu_createlist\"\n\t\t\tandroid:icon=\"@drawable/folder_plus_24dp\"\n\t\t\tandroid:orderInCategory=\"997\"\n\t\t\tapp:showAsAction=\"ifRoom\"\n\t\t\tandroid:title=\"@string/menu_createlist\"/>\n\t</group>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_preferences\"\n\t\tandroid:icon=\"@drawable/ic_settings_24dp\"\n\t\tandroid:orderInCategory=\"999\"\n\t\tapp:showAsAction=\"never\"\n\t\tandroid:title=\"@string/menu_preferences\"/>\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/fragment_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- Shown only in the \"archive of deleted notes\" activity -->\n<menu\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\t<item android:id=\"@+id/menu_search\"\n\t\tandroid:icon=\"@drawable/ic_search_24dp\"\n\t\tandroid:orderInCategory=\"0\"\n\t\tapp:showAsAction=\"always\"\n\t\tandroid:title=\"@string/search_hint\"\n\t\tapp:actionViewClass=\"androidx.appcompat.widget.SearchView\"/>\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/fragment_tasklist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\t<group android:id=\"@+id/list_menu_group\">\n\n\t\t<item\n\t\t\tandroid:id=\"@+id/menu_clearcompleted\"\n\t\t\tandroid:orderInCategory=\"300\"\n\t\t\tandroid:icon=\"@drawable/ic_notebook_minus_24dp\"\n\t\t\tandroid:title=\"@string/menu_clearcompleted\"\n\t\t\tapp:showAsAction=\"ifRoom\"/>\n\n\t\t<item\n\t\t\tandroid:icon=\"@drawable/ic_sort_24dp\"\n\t\t\tandroid:orderInCategory=\"400\"\n\t\t\tapp:showAsAction=\"ifRoom\"\n\t\t\tandroid:title=\"@string/sorting\">\n\t\t\t<menu>\n\t\t\t\t<item\n\t\t\t\t\tandroid:id=\"@+id/menu_sort_title\"\n\t\t\t\t\tandroid:title=\"@string/sort_list_alphabetical\"/>\n\t\t\t\t<item\n\t\t\t\t\tandroid:id=\"@+id/menu_sort_due\"\n\t\t\t\t\tandroid:title=\"@string/sort_list_due\"/>\n\t\t\t\t<item\n\t\t\t\t\tandroid:id=\"@+id/menu_sort_manual\"\n\t\t\t\t\tandroid:title=\"@string/sort_list_manual\"/>\n\t\t\t</menu>\n\t\t</item>\n\n\t</group>\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/fragment_tasklist_context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- actionbar menu when you long-click a note in the list -->\n<menu xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item\n\t\tandroid:id=\"@+id/menu_delete\"\n\t\tandroid:icon=\"@drawable/ic_delete_24dp\"\n\t\tapp:showAsAction=\"ifRoom\"\n\t\tandroid:title=\"@string/menu_delete\"/>\n\t<item\n\t\tandroid:id=\"@+id/menu_switch_list\"\n\t\tandroid:icon=\"@drawable/folder_move_24dp\"\n\t\tandroid:title=\"@string/move\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_share\"\n\t\tandroid:icon=\"@drawable/ic_share_24dp\"\n\t\tapp:showAsAction=\"ifRoom\"\n\t\tandroid:title=\"@string/menu_share\"/>\n\t<item\n\t\tandroid:id=\"@+id/menu_copy\"\n\t\tandroid:icon=\"@drawable/ic_copy_24dp\"\n\t\tapp:showAsAction=\"ifRoom\"\n\t\tandroid:title=\"@android:string/copy\"/>\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/fragment_tasklists_viewpager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<!-- this menu is shown only in the main activity -->\n<menu\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\t<!-- Outside group to allow for action bar placement -->\n\t<item\n\t\tandroid:id=\"@+id/menu_search\"\n\t\tandroid:icon=\"@drawable/ic_search_24dp\"\n\t\tandroid:orderInCategory=\"200\"\n\t\tandroid:title=\"@string/search_hint\"\n\t\tapp:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n\t\tapp:showAsAction=\"ifRoom|collapseActionView\"/>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_sync\"\n\t\tandroid:icon=\"@drawable/ic_refresh_24dp\"\n\t\tandroid:orderInCategory=\"201\"\n\t\tandroid:title=\"@string/menu_sync\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\t<!-- <item\n\t\tandroid:id=\"@+id/menu_createlist\"\n\t\tandroid:icon=\"?ic_action_add_list\"\n\t\tandroid:orderInCategory=\"202\"\n\t\tapp:showAsAction=\"ifRoom\"\n\t\tandroid:title=\"@string/menu_createlist\"/> -->\n\n\t<group android:id=\"@+id/viewpager_menu_group\">\n\n\t\t<item\n\t\t\tandroid:id=\"@+id/menu_deletedtasks\"\n\t\t\tandroid:orderInCategory=\"203\"\n\t\t\tandroid:icon=\"@drawable/ic_archive_24dp\"\n\t\t\tandroid:title=\"@string/view_deletedtasks\"\n\t\t\tapp:showAsAction=\"ifRoom\"/>\n\t</group>\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/fragment_tasks_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<menu xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- Outside group to allow for action bar placement -->\n\t<item\n\t\tandroid:id=\"@+id/menu_delete\"\n\t\tandroid:icon=\"@drawable/ic_delete_24dp\"\n\t\tandroid:orderInCategory=\"100\"\n\t\tandroid:title=\"@string/menu_delete\"\n\t\tapp:showAsAction=\"ifRoom\">\n\t</item>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_revert\"\n\t\tandroid:icon=\"@drawable/ic_undo_24dp\"\n\t\tandroid:orderInCategory=\"101\"\n\t\tandroid:title=\"@android:string/cancel\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_share\"\n\t\tandroid:icon=\"@drawable/ic_share_24dp\"\n\t\tandroid:orderInCategory=\"102\"\n\t\tandroid:title=\"@string/menu_share\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_lock\"\n\t\tandroid:icon=\"@drawable/ic_lock_closed_24dp\"\n\t\tandroid:orderInCategory=\"103\"\n\t\tandroid:title=\"@string/lock_note\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\n\t<item\n\t\tandroid:id=\"@+id/menu_unlock\"\n\t\tandroid:icon=\"@drawable/ic_lock_open_24dp\"\n\t\tandroid:orderInCategory=\"104\"\n\t\tandroid:title=\"@string/unlock_note\"\n\t\tapp:showAsAction=\"ifRoom\"/>\n\n\t<group android:id=\"@+id/editor_menu_group\">\n\t\t<item\n\t\t\tandroid:id=\"@+id/menu_timemachine\"\n\t\t\tandroid:orderInCategory=\"105\"\n\t\t\tandroid:title=\"@string/timemachine\"\n\t\t\tapp:showAsAction=\"never\"/>\n\t</group>\n\n</menu>"
  },
  {
    "path": "app/src/main/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nCopyright (C) 2014 Jonas Kalderstam\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n\t<!-- shown to the user -->\n\t<string-array name=\"theme_preference\">\n\t\t<item>@string/settings_summary_theme_light</item>\n\t\t<item>@string/settings_summary_theme_dark</item>\n\t\t<item>@string/settings_summary_theme_classic</item>\n\t\t<item>@string/settings_summary_theme_black</item>\n\t</string-array>\n\t<!-- Used by the code -->\n\t<string-array name=\"themevalues_preference\" translatable=\"false\">\n\t\t<item>@string/const_theme_light_ab</item>\n\t\t<item>@string/const_theme_googlenow_dark</item>\n\t\t<item>@string/const_theme_classic</item>\n\t\t<item>@string/const_theme_black</item>\n\t</string-array>\n\t<string-array name=\"widget_theme_preference\">\n\t\t<item>@string/settings_summary_theme_dark</item>\n\t\t<item>@string/settings_summary_theme_light</item>\n\t</string-array>\n\t<string-array name=\"widget_themevalues_preference\" tools:ignore=\"MissingTranslation\">\n\t\t<item>@string/const_theme_dark</item>\n\t\t<item>@string/const_theme_light</item>\n\t</string-array>\n\t<string-array name=\"sorting_preference\">\n\t\t<item>@string/sort_list_alphabetical</item>\n\t\t<item>@string/sort_list_due</item>\n\t\t<item>@string/sort_list_updated</item>\n\t\t<item>@string/sort_list_manual</item>\n\t</string-array>\n\t<string-array name=\"sortingvalues_preference\" tools:ignore=\"MissingTranslation\">\n\t\t<item>@string/const_alphabetic</item>\n\t\t<item>@string/const_duedate</item>\n\t\t<item>@string/const_modified</item>\n\t\t<item>@string/const_possubsort</item>\n\t</string-array>\n\t<string-array name=\"show_list_as\">\n\t\t<item>@string/default_style</item>\n\t\t<item>@string/show_items_as_tasks</item>\n\t\t<item>@string/show_items_as_notes</item>\n\t</string-array>\n\t<string-array name=\"listtype_preference\">\n\t\t<item>@string/show_items_as_tasks</item>\n\t\t<item>@string/show_items_as_notes</item>\n\t</string-array>\n\t<string-array name=\"listtypevalues_preference\">\n\t\t<item>@string/const_listtype_tasks</item>\n\t\t<item>@string/const_listtype_notes</item>\n\t</string-array>\n\t<string-array name=\"sort_list_by\">\n\t\t<item>@string/sort_list_default</item>\n\t\t<item>@string/sort_list_alphabetical</item>\n\t\t<item>@string/sort_list_updated</item>\n\t\t<item>@string/sort_list_due</item>\n\t\t<item>@string/sort_list_manual</item>\n\t</string-array>\n\n\t<!-- options shown on the preferences pages. These ones define the possibile date formats\n\t  for the spinner on the task detail page. New formats are added only to this array -->\n\t<string-array name=\"dateformat_long_values\">\n\t\t<item>@string/dateformat_long_1</item>\n\t\t<item>@string/dateformat_long_2</item>\n\t\t<item>@string/dateformat_long_3</item>\n\t\t<item>@string/dateformat_long_4</item>\n\t\t<item>@string/dateformat_long_5</item>\n\t</string-array>\n\n\t<string-array name=\"dateformat_short_values\">\n\t\t<item>@string/dateformat_short_1</item>\n\t\t<item>@string/dateformat_short_2</item>\n\t\t<item>@string/dateformat_short_3</item>\n\t\t<item>@string/dateformat_short_4</item>\n\t\t<item>@string/dateformat_short_5</item>\n\t\t<item>@string/dateformat_short_6</item>\n\t\t<item>@string/dateformat_short_7</item>\n\t\t<item>@string/dateformat_short_8</item>\n\t\t<item>@string/dateformat_short_9</item>\n\t</string-array>\n\n\t<!-- <attr name=\"titleFontFamily\" format=\"enum\">\n\t\t\t<enum name=\"robotosans\" value=\"0\" />\n\t\t\t<enum name=\"robotocondensed\" value=\"1\" />\n\t\t\t<enum name=\"robotolight\" value=\"2\" />\n\t\t\t<enum name=\"robotothin\" value=\"3\" />\n\t\t</attr>\n\t\t<attr name=\"titleFontStyle\" format=\"enum\">\n\t\t\t<enum name=\"normal\" value=\"0\" />\n\t\t\t<enum name=\"bold\" value=\"1\" />\n\t\t\t<enum name=\"italic\" value=\"2\" />\n\t\t</attr> -->\n\t<string-array name=\"fontfamily_entries\">\n\t\t<item>Roboto</item>\n\t\t<item>Roboto Condensed</item>\n\t\t<item>Roboto Light</item>\n\t\t<item>Roboto Thin</item>\n\t</string-array>\n\t<string-array name=\"fontfamily_values\">\n\t\t<item>0</item>\n\t\t<item>1</item>\n\t\t<item>2</item>\n\t\t<item>3</item>\n\t</string-array>\n\t<string-array name=\"fontstyle_entries\">\n\t\t<item>@string/normal</item>\n\t\t<item>@string/bold</item>\n\t\t<item>@string/italic</item>\n\t</string-array>\n\t<string-array name=\"fontstyle_values\">\n\t\t<item>0</item>\n\t\t<item>1</item>\n\t\t<item>2</item>\n\t</string-array>\n\t<string-array name=\"fontsize_entries\">\n\t\t<item>@string/small</item>\n\t\t<item>@string/medium</item>\n\t\t<item>@string/large</item>\n\t</string-array>\n\t<string-array name=\"fontsize_values\">\n\t\t<item>0</item>\n\t\t<item>1</item>\n\t\t<item>2</item>\n\t</string-array>\n\n\t<string-array name=\"notification_prio_entries\">\n\t\t<item>@string/notification_prio_high</item>\n\t\t<item>@string/standard</item>\n\t\t<item>@string/notification_prio_low</item>\n\t</string-array>\n\t<string-array name=\"notification_prio_values\">\n\t\t<item>2</item>\n\t\t<item>0</item>\n\t\t<item>-2</item>\n\t</string-array>\n\n\t<!-- use   ./gradlew checkLanguages   to see if it's up to date  -->\n\t<string-array name=\"translated_langs\" tools:ignore=\"MissingTranslation\">\n\t\t<item>af</item>\n\t\t<item>ar</item>\n\t\t<item>bg</item>\n\t\t<item>ca_ES</item>\n\t\t<item>co</item>\n\t\t<item>cs</item>\n\t\t<item>da</item>\n\t\t<item>de</item>\n\t\t<item>el</item>\n\t\t<item>en</item>\n\t\t<item>es</item>\n\t\t<item>et</item>\n\t\t<item>fa</item>\n\t\t<item>fi_FI</item>\n\t\t<item>fr</item>\n\t\t<item>gl</item>\n\t\t<item>hu</item>\n\t\t<item>in_ID</item>\n\t\t<item>is</item>\n\t\t<item>it</item>\n\t\t<item>jw_IL</item>\n\t\t<item>ja</item>\n\t\t<item>ko</item>\n\t\t<item>nb_NO</item>\n\t\t<item>nl</item>\n\t\t<item>pl</item>\n\t\t<item>pt</item>\n\t\t<item>pt_BR</item>\n\t\t<item>pt_PT</item>\n\t\t<item>ro</item>\n\t\t<item>ru</item>\n\t\t<item>sk</item>\n\t\t<item>sl</item>\n\t\t<item>sv</item>\n\t\t<item>ta</item>\n\t\t<item>tr</item>\n\t\t<item>uk</item>\n\t\t<item>vec</item>\n\t\t<item>vi</item>\n\t\t<item>zh_CN</item>\n\t\t<item>zh_TW</item>\n\t</string-array>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/attr.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n<resources>\n\n\t<declare-styleable name=\"StyledTextView\">\n\t\t<attr name=\"linkify\" format=\"boolean\"/>\n\t\t<attr name=\"styledText\" format=\"string\"/>\n\t\t<attr name=\"titleRelativeSize\" format=\"float\"/>\n\t\t<attr name=\"titleFontFamily\" format=\"enum\">\n\t\t\t<enum name=\"robotosans\" value=\"0\"/>\n\t\t\t<enum name=\"robotocondensed\" value=\"1\"/>\n\t\t\t<enum name=\"robotolight\" value=\"2\"/>\n\t\t\t<enum name=\"robotothin\" value=\"3\"/>\n\t\t</attr>\n\t\t<attr name=\"titleFontStyle\" format=\"enum\">\n\t\t\t<enum name=\"normal\" value=\"0\"/>\n\t\t\t<enum name=\"bold\" value=\"1\"/>\n\t\t\t<enum name=\"italic\" value=\"2\"/>\n\t\t</attr>\n\t\t<attr name=\"bodyFontFamily\" format=\"enum\">\n\t\t\t<enum name=\"robotosans\" value=\"0\"/>\n\t\t\t<enum name=\"robotocondensed\" value=\"1\"/>\n\t\t\t<enum name=\"robotolight\" value=\"2\"/>\n\t\t\t<enum name=\"robotothin\" value=\"3\"/>\n\t\t</attr>\n\t\t<attr name=\"secondaryColor\" format=\"color\"/>\n\t</declare-styleable>\n\n\t<!-- see DelegateFrame.java -->\n\t<declare-styleable name=\"DelegateFrame\">\n\t\t<attr name=\"enlargedView\" format=\"reference\"/>\n\t</declare-styleable>\n\n\t<!-- Vetted -->\n\t<attr name=\"my_list_selector\" format=\"reference\"/>\n\t<attr name=\"editorBackgroundColor\" format=\"reference\"/>\n\t<attr name=\"img_default_selector\" format=\"reference\"/>\n\n\t<attr name=\"viewpager_bg\" format=\"reference\"/>\n\t<attr name=\"list_item_card_background\" format=\"reference\"/>\n\t<attr name=\"labelAddItemTextColor\" format=\"reference|color\"/>\n\t<attr name=\"drag_handle_color\" format=\"reference|color\"/>\n\n\t<attr name=\"TaskListItem\" format=\"reference\"/>\n\t<attr name=\"ListDividerStyle\" format=\"reference\"/>\n\t<attr name=\"ListMarginStyle\" format=\"reference\"/>\n\t<attr name=\"LeftDrawerStyle\" format=\"reference\"/>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/bool.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<resources>\n\n\t<!-- Scrollview in editor -->\n\t<bool name=\"fillEditor\">true</bool>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n<resources>\n\t<!-- Things I confirm to be useful -->\n\n\t<!-- TODO don't use this in android 13. users want to pick their own accent color.\n\t      Or make a new theme that doesn't define this. whatever is better -->\n\t<color name=\"accent\">@android:color/holo_blue_dark</color>\n\n\t<!-- Make icons black for light themes (85% transparency) -->\n\t<color name=\"black_alpha85\">#D9000000</color>\n\n\t<!-- Make icons white for dark themes (85% transparency) -->\n\t<color name=\"white_alpha85\">#D9FFFFFF</color>\n\n\t<!-- it's supposed to be the same background color that you get when you hold your finger\n\t on a clickable textview, like the due date box in the task detail view. If you find a\n\t  style attribute to use instead of this hardcoded value, check out btn_toggle_bg.xml -->\n\t<color name=\"widget_pressed_background\">#40808080</color>\n\n\t<!-- (also) to distinguish UI items during development -->\n\t<color name=\"material_red\">#ffff1744</color>\n\t<color name=\"green\">#4CAF50</color>\n\n\t<!-- Unchecked. some of these should be deleted -->\n\n\t<!-- 12% black in LIGHT THEME -->\n\t<!-- 12% white in BLACK THEME -->\n\t<!-- Should be lighter/darker than above -->\n\t<color name=\"googlenow_grey\">#e5e5e5</color>\n\t<color name=\"googlenow_grey_trans\">#aae5e5e5</color>\n\t<color name=\"googlenow_darkergrey\">#1d1d1d</color>\n\t<color name=\"googlenow_darkergrey_trans\">#aa1d1d1d</color>\n\t<color name=\"background_holo_light\">#fff3f3f3</color>\n\t<color name=\"labelItemTextColorLight\">#555555</color>\n\t<color name=\"labelItemTextColorDark\">#cccccc</color>\n\t<color name=\"dragHandleColorLight\">#30000000</color>\n\t<color name=\"dragHandleColorDark\">#8affffff</color>\n\t<color name=\"completedGrey\">#aa888888</color>\n\t<color name=\"uncheckedGrey\">#aa888888</color>\n\t<!-- Generated by https://www.materialpalette.com/orange/deep-orange -->\n</resources>"
  },
  {
    "path": "app/src/main/res/values/constants.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- Provides constants and default values -->\n<resources translatable=\"false\">\n\n\t<string name=\"const_all_default_sorting\">@string/const_alphabetic</string>\n\n\t<integer name=\"layout_horizontal\">0</integer>\n\t<integer name=\"layout_vertical\">1</integer>\n\t<integer name=\"layout_best_orientation\">@integer/layout_vertical</integer>\n\n\t<integer name=\"default_editor_font_size\">22</integer>\n\n\t<!-- For when they HAVE been completed, for AndroidAgendaWidget primarily -->\n\t<string name=\"note_completed_broadcast_intent\">com.nononsenseapps.notecompleted</string>\n\n\t<!-- Theme constants should contain \"light\" if a light timepicker is appropriate -->\n\t<string name=\"const_theme_dark\">dark</string>\n\t<string name=\"const_theme_black\">black</string>\n\t<string name=\"const_theme_light\">light</string>\n\t<string name=\"const_theme_classic\">light_classic</string>\n\t<string name=\"const_theme_light_ab\">light_ab</string>\n\t<string name=\"const_theme_googlenow_dark\">googlenow_dark</string>\n\n\t<string name=\"const_alphabetic\">title COLLATE NOCASE</string>\n\t<string name=\"const_as_alphabetic\">%s COLLATE NOCASE</string>\n\t<string name=\"const_duedate\">duedate</string>\n\t<string name=\"const_modified\">modified</string>\n\t<string name=\"const_possubsort\">manual</string>\n\t<string name=\"default_sorttype\">@string/const_possubsort</string>\n\t<string name=\"const_listtype_tasks\">astasks</string>\n\t<string name=\"const_listtype_notes\">asnotes</string>\n\t<string name=\"default_listtype\">@string/const_listtype_tasks</string>\n\t<string name=\"pref_listtype\">listtype</string>\n\t<string name=\"const_sans\">Sans</string>\n\t<string name=\"const_serif\">Serif</string>\n\t<string name=\"const_monospace\">Monospace</string>\n\t<string name=\"key_pref_notif_channel_settings\">key_pref_notif_channel_settings</string>\n\t<string name=\"key_pref_cat_notif_old\">key_pref_cat_notif_old</string>\n\t<string name=\"key_pref_ringtone\">global_ringtone</string>\n\t<string name=\"key_pref_vibrate\">global_vibrate</string>\n\t<string name=\"key_pref_prio\">global_prio</string>\n\t<string name=\"key_pref_should_use_exact_alarms\">key_pref_should_use_exact_alarms</string>\n\t<string name=\"key_pref_ignore_battery_optimizations\">ignoreBatteryOptimizationPref</string>\n\t<string name=\"key_pref_allow_exact_reminders\">key_pref_allow_exact_reminders</string>\n\t<string name=\"key_pref_disable_hibernation\">key_pref_disable_hibernation</string>\n\t<string name=\"key_pref_notif_visibility\">key_pref_notif_visibility</string>\n\t<string name=\"key_pref_sync_enabled_master\">key_pref_sync_enabled_master</string>\n\t<string name=\"pref_locale\">pref_key_locale</string>\n\t<string name=\"key_pref_item_max_height\">item_max_height</string>\n\t<string name=\"key_pref_dateformat_long\">dateformat_long</string>\n\t<string name=\"key_pref_dateformat_short\">dateformat_short</string>\n\n\t<string name=\"pref_editor_title_fontfamily\">pref_editor_title_fontfamily</string>\n\t<string name=\"pref_editor_title_fontstyle\">pref_editor_title_fontstyle</string>\n\t<string name=\"pref_editor_body_fontfamily\">pref_editor_body_fontfamily</string>\n\t<string name=\"pref_editor_links\">pref_editor_links</string>\n\t<string name=\"pref_editor_fontsize\">pref_editor_fontsize</string>\n\t<string name=\"pref_editor_biggertitles\">pref_editor_biggertitles</string>\n\n\t<string name=\"pref_list_title_fontfamily\">pref_restart_list_title_fontfamily</string>\n\t<string name=\"pref_list_title_fontstyle\">pref_restart_list_title_fontstyle</string>\n\t<string name=\"pref_list_body_fontfamily\">pref_restart_list_body_fontfamily</string>\n\t<string name=\"pref_list_links\">pref_restart_list_links</string>\n\t<string name=\"pref_list_fontsize\">pref_restart_list_fontsize</string>\n\n\t<string name=\"pref_sorttype\">key_sort_type</string>\n\t<string name=\"pref_defaultlist\">key_default_list_id</string>\n\t<string name=\"pref_defaultstartlist\">key_default_list_to_open_id</string>\n\t<string name=\"pref_hidecheckboxes\">key_hiddencheckbox</string>\n\t<string name=\"preferences_week_start_day_default\">-1</string>\n\t<string name=\"dashclock_nononsense_notes\">NoNonsense Notes</string>\n\t<string name=\"dashclock_tasks_count\">%d</string>\n\t<!-- These are intended for the upper_limit_values array -->\n\t<string name=\"dashclock_pref_today\">Today</string>\n\t<string name=\"dashclock_pref_tomorrow\">Tomorrow</string>\n\t<string name=\"dashclock_pref_next7\">Next7</string>\n\t<string name=\"dashclock_pref_none\">None</string>\n\n\t<!-- URLs -->\n\t<string name=\"github_repo_url\" translatable=\"false\">https://github.com/spacecowboy/NotePad</string>\n\n\t<!-- Date formats. \"localtime\" is replaced at runtime with 12/24 hour clock.\n\t They're hardcoded and untranslatable because if translators make mistakes,\n\t the app will crash at runtime, and I won't check those strings on every pull request.\n\t In \"Friday 12:59 AM, 27 april 2023\", you have:\n\t  E = \"Fri\", MMM = \"Apr\", d = dd = \"27\", localtime = \"12:59 AM\",\n\t  EEEE = \"Friday\", MMMM = \"April\", MM = 04, yyyy = 2023, yy = 23,\n\t  allowed:   `/(   forbidden:   O'\n\t-->\n\t<string name=\"dateformat_weekday\">EEEE</string>\n\t<!-- for the date in a reminder item in the task detail page -->\n\t<string name=\"dateformat_just_date\">MMMM d</string>\n\t<!-- for the big widget -->\n\t<string name=\"dateformat_micro\">MMM d</string>\n\n\t<!-- short date formats, for the note item in the list. Used in arrays.xml -->\n\t<string name=\"dateformat_short_1\">E localtime, d MMM</string>\n\t<string name=\"dateformat_short_2\">E, d MMM</string>\n\t<string name=\"dateformat_short_3\">MMM d</string>\n\t<string name=\"dateformat_short_4\">E, localtime</string>\n\t<string name=\"dateformat_short_5\">d MMM</string>\n\t<string name=\"dateformat_short_6\">MM/dd (E)</string>\n\t<string name=\"dateformat_short_7\">MM/dd</string>\n\t<string name=\"dateformat_short_8\">E d MMM yyyy</string>\n\t<string name=\"dateformat_short_9\">E d MMM `yy</string>\n\n\t<!-- long date formats, for the spinner in detailed view. Used in arrays.xml -->\n\t<string name=\"dateformat_long_1\">EEEE localtime, MMMM d</string>\n\t<string name=\"dateformat_long_2\">E localtime, MMM d</string>\n\t<string name=\"dateformat_long_3\">EEEE d MMMM</string>\n\t<string name=\"dateformat_long_4\">E d MMM</string>\n\t<string name=\"dateformat_long_5\">E, d MMM yyyy</string>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dashclock_preference_values.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string-array name=\"due_upper_limit_entries\">\n\t\t<item>@string/dashclock_today</item>\n\t\t<item>@string/dashclock_tomorrow</item>\n\t\t<item>@string/dashclock_next7</item>\n\t\t<item>@string/dashclock_anytime</item>\n\t</string-array>\n\n\t<string-array name=\"due_upper_limit_values\">\n\t\t<item>@string/dashclock_pref_today</item>\n\t\t<item>@string/dashclock_pref_tomorrow</item>\n\t\t<item>@string/dashclock_pref_next7</item>\n\t\t<item>@string/dashclock_pref_none</item>\n\t</string-array>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<resources>\n\n\t<!-- Stuff -->\n\t<dimen name=\"drag_grip_ridge_size\">4dp</dimen>\n\t<dimen name=\"drag_grip_ridge_gap\">2dp</dimen>\n\t<dimen name=\"drag_grip_width\">32dp</dimen>\n\t<!-- it is 16dp from the edge, 8 dp padding always presetn -->\n\t<dimen name=\"drag_grip_avoid_padding\">16dp</dimen>\n\n\t<!-- List stuff -->\n\t<dimen name=\"listMargins\">0dp</dimen>\n\t<dimen name=\"listMarginsPadded\">8dp</dimen>\n\n\t<!-- list item min size -->\n\t<dimen name=\"list_item_min_size\">48dp</dimen>\n\t<dimen name=\"widget_item_min_size\">35dp</dimen> <!-- TODO try 48 -->\n\n\t<!-- Preference activity side margins -->\n\t<dimen name=\"preference_screen_side_margin\">8dp</dimen>\n\n\t<!-- For widget -->\n\t<dimen name=\"widget_margin\">8dp</dimen>\n\n\t<!-- WidgetConfig -->\n\t<dimen name=\"widget_conf_preview_land_width\">250dp</dimen>\n\t<dimen name=\"widget_conf_preview_port_height\">150dp</dimen>\n\t<dimen name=\"layout_match_parent\">-1dp</dimen>\n\t<!-- Changes depending on screen values -->\n\t<dimen name=\"widget_conf_preview_width\">@dimen/layout_match_parent</dimen>\n\t<dimen name=\"widget_conf_preview_height\">@dimen/widget_conf_preview_port_height</dimen>\n\n\t<!-- Editor fragment stuff -->\n\t<dimen name=\"editor_side_margin\">0dp</dimen>\n\t<dimen name=\"editor_vertical_margin\">0dp</dimen>\n\t<dimen name=\"editor_vertical_margin_bottom\">0dp</dimen>\n\n\t<!-- For card in editor -->\n\n\t<!-- Hacky but it works! -->\n\t<dimen name=\"activity_lone_horizontal_margin\">0dp</dimen>\n\t<dimen name=\"activity_lone_vertical_margin\">16dp</dimen>\n\t<dimen name=\"activity_lone_horizontal_padding\">16dp</dimen>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/layout_constants.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<resources>\n\n\t<integer name=\"leftFragmentWeight\">1</integer>\n\t<integer name=\"rightFragmentWeight\">1</integer>\n\t<!-- Only one fragment visible at a time -->\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<resources>\n\t<string name=\"nononsense_notes\" translatable=\"false\">NoNonsense Notes</string>\n\t<!-- For the label in the app launcher -->\n\t<string name=\"app_name_short\">Notes</string>\n\t<string name=\"title_create\">New note</string>\n\t<string name=\"timemachine\">Time machine</string>\n\t<string name=\"time\">Time</string>\n\t<string name=\"done\">Done</string>\n\t<string name=\"saved\">Saved</string>\n\t<string name=\"menu_save_and_add\">Save and add note</string>\n\t<string name=\"menu_delete\">Delete</string>\n\t<string name=\"menu_deletelist\">Delete list</string>\n\t<string name=\"menu_sync\">Sync</string>\n\t<string name=\"menu_share\">Share</string>\n\t<string name=\"menu_preferences\">Settings</string>\n\t<string name=\"menu_createlist\">Start new list</string>\n\t<string name=\"menu_clearcompleted\">Clear completed</string>\n\t<string name=\"menu_setdefaultlist\">Set as default list</string>\n\t<string name=\"new_default_set\">New default set</string>\n\t<string name=\"menu_managelists\">Edit lists</string>\n\t<string name=\"add_item\">Add item</string>\n\t<string name=\"delete_question\">Delete?</string>\n\t<string name=\"delete_items_message\">Delete these items?</string>\n\t<string name=\"delete_item_message\">Delete this item?</string>\n\t<string name=\"delete_list_message\">Delete this list?</string>\n\t<string name=\"delete_completed_tasks_question\">Delete all completed tasks?</string>\n\t<string name=\"editor_due_date_hint\">Due date</string>\n\t<string name=\"editor_title_hint\">Title</string>\n\t<string name=\"editor_note_hint\">Note</string>\n\t<string name=\"resolve_edit\">Edit note</string>\n\t<string name=\"default_style\">Default style</string>\n\t<string name=\"show_items_as_tasks\">checkable tasks</string>\n\t<string name=\"show_items_as_notes\">simple notes</string>\n\t<string name=\"sort_list_default\">Default sorting order</string>\n\t<string name=\"sort_list_alphabetical\">A-Z</string>\n\t<string name=\"sort_list_due\">Due date</string>\n\t<string name=\"sort_list_updated\">Latest updated</string>\n\t<string name=\"sort_list_manual\">Manual</string>\n\t<string name=\"search_description\">Notes and tasks</string>\n\t<string name=\"archive\">Archive</string>\n\t<string name=\"view_deletedtasks\" translatable=\"false\">@string/archive</string>\n\t<string name=\"restore\">Restore</string>\n\t<string name=\"restore_to\">Restore to</string>\n\t<string name=\"search_hint\">Search</string>\n\t<string name=\"settings_theme\">Current theme</string>\n\t<string name=\"settings_theme_dialog\">Use theme</string>\n\t<string name=\"settings_lang\">Language</string>\n\t<string name=\"settings_lang_dialog\" translatable=\"false\">@string/settings_lang</string>\n\t<string name=\"settings_summary_theme_black\">Black</string>\n\t<string name=\"settings_summary_theme_dark\">Dark</string>\n\t<string name=\"settings_summary_theme_light\">Light</string>\n\t<string name=\"settings_summary_theme_classic\">Classic</string>\n\t<string name=\"settings_cat_appearance\">Appearance</string>\n\t<string name=\"preference_preview_text\">This is how the editor text will appear</string>\n\t<string name=\"settings_account_title\">Select an account</string>\n\t<string name=\"settings_account_summary\">Touch to switch account</string>\n\n\t<string name=\"settings_cat_syncing\">Syncing</string>\n\t<string name=\"show_from_all_lists\">All lists</string>\n\t<string name=\"lists\">Lists</string>\n\t<string name=\"deleted\">Deleted</string>\n\t<string name=\"repeat\">Repeat</string>\n\t<string name=\"once\">Once</string>\n\t<string name=\"always\">Always</string>\n\t<string name=\"permission_read_label\">read notes and lists</string>\n\t<string name=\"permission_read_desc\">Allows the app to read your notes and related lists in No Nonsense Notes</string>\n\t<string name=\"permission_write_label\">modify notes and lists</string>\n\t<string name=\"permission_write_desc\">Allows the app to insert, update and delete notes and lists in No Nonsense Notes</string>\n\t<string name=\"settings_list_dialog\">Select a list</string>\n\t<string name=\"settings_list\">List</string>\n\t<string name=\"drag_to_timetravel\">Drag to time travel</string>\n\t<string name=\"navigation_drawer_open\">Open navigation drawer</string>\n\t<string name=\"navigation_drawer_close\">Close navigation drawer</string>\n\t<string name=\"import_data_question\">Import data?</string>\n\t<string name=\"import_started\">Importing data…</string>\n\t<string name=\"imported_result\">Imported %1$d notes into %2$d lists</string>\n\t<string name=\"import_error\">Something went wrong: %s</string>\n\n\t<!-- new strings added since 2022 -->\n\t<string name=\"notification_channel_name\">Reminders for notes</string>\n\t<string name=\"notification_channel_description\">Shows the note content at the time of the chosen reminder</string>\n\t<string name=\"feature_is_WIP\">This feature is a W.I.P.</string>\n\t<string name=\"permission_denied\">Can not proceed: you denied the permission</string>\n\t<string name=\"no_sync_method_chosen\">You did not choose a sync method</string>\n\t<string name=\"file_picker_not_available\">The file picker is not available</string>\n\t<string name=\"choose_backup_folder\">Choose a backup folder</string>\n\t<string name=\"use_exact_alarms\">Use exact alarms</string>\n\t<string name=\"exact_alarms_summary\">Uses more battery to show notification reminders in a more reliable way</string>\n\t<string name=\"disable_battery_optimizations\">Turn off battery optimizations</string>\n\t<string name=\"battery_optimizations_active\">Battery optimizations on</string>\n\t<string name=\"battery_optimizations_inactive\">Battery optimizations off</string>\n\t<string name=\"allow_exact_reminders\">Allow exact reminders</string>\n\t<string name=\"allow_exact_reminders_summary\">Open a page to allow this app to send reminders more reliably. Only needed since Android 12 \"Snow Cone\"</string>\n\t<string name=\"for_older_devices\">For older Android devices</string>\n\t<string name=\"overwritten_in_newer_systems\">Can be undone by certain notification channel settings</string>\n\t<string name=\"prefs_improved_reliability\">Preferences to improve reliability</string>\n\t<string name=\"notification_channel_settings\">Notification channel settings</string>\n\t<string name=\"open_channel_settings_description\">Open a page to edit the notification settings for this app. These override any settings chosen elsewhere</string>\n\t<string name=\"libraries_used\">Libraries</string>\n\t<string name=\"app_about_dev_team\">Created by Jonas Kalderstam and maintained by Campello Manuel</string>\n\t<string name=\"app_about_git_repo\">This app is copylefted libre software, available at https://github.com/spacecowboy/NotePad</string>\n\t<string name=\"app_about_license\">Licensed GPLv3+, https://www.gnu.org/licenses</string>\n\t<string name=\"app_about_bugreports\">Report bugs and issues at https://github.com/spacecowboy/NotePad/issues</string>\n\t<string name=\"app_about_translations\">Help translate the app at https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n\t<string name=\"sponsor_this_project\" translatable=\"false\">\\\"Sponsor this project\\\"</string>\n\t<string name=\"app_about_donations\">We accept donations. To support our work, look for %1$s in %2$s</string>\n\t<string name=\"disable_android_hibernation\">Disable Android hibernation</string>\n\t<string name=\"disable_android_hibernation_desc\">It is a feature that makes the app less reliable. It may block your reminders. Open this, look for something like \\\"suspend app activity if unused\\\" and turn it off</string>\n\t<string name=\"msg_hibernation_already_off\">Android hibernation is already OFF, you do not need this</string>\n\t<string name=\"msg_enable_notifications\">Go to settings -> notifications to enable notifications</string>\n\t<string name=\"notifications_visibility\">Notifications visibility</string>\n\t<string name=\"notifications_enabled\">Notifications are visible</string>\n\t<string name=\"notifications_blocked\">Notifications are not visible, so you will not see the reminders. Touch here, find the permissions category, and enable notifications for this app</string>\n\t<string name=\"unavailable_chose_directory\">Unavailable. Please choose a directory first</string>\n\t<string name=\"not_selected_yet\">Not selected yet</string>\n\t<string name=\"enable_sync\">Enable synchronization</string>\n\t<string name=\"enable_sync_desc\">Enables synchronization features and allows to choose a sync source. Backups are not affected</string>\n\t<string name=\"changelog_online\">Changelog (online)</string>\n\t<string name=\"canceled_note_locked\">Cancelled: the note is locked</string>\n\t<string name=\"order_notes_by\">Order notes by:</string>\n\t<string name=\"show_elements_as\">Show items as:</string>\n\t<string name=\"version\">Version: %s</string>\n\t<string name=\"next_month\">Next month</string>\n\t<string name=\"next_year\">Next year</string>\n\t<string name=\"select_all\">Select all</string>\n\t<string name=\"tutorial_online\">Tutorial (online)</string>\n\t<string name=\"showcase_tutorial_title\">Feeling lost?</string>\n\t<string name=\"showcase_tutorial_description\">We have an online tutorial. Find it in the settings</string>\n\t<string name=\"welcome_note_title\">Welcome!</string>\n\t<string name=\"welcome_note_row_2\">Open this to get started.</string>\n\t<string name=\"welcome_note_row_3\">This app has a detailed tutorial. You can find it at</string>\n\t<string name=\"unsupported_readonly_file\">Unsupported read-only file: %s</string>\n\t<string name=\"show_completed_notes\">Show completed notes</string>\n\n\t<!--\n\t\tUse few if language need it. In English is the same.\n\t\t<item quantity=\"few\">Copied 2 notes.</item>\n\t-->\n\t<plurals name=\"notecopied_msg\" translatable=\"false\">\n\t\t<item quantity=\"one\">@string/notecopied_one</item>\n\t\t<item quantity=\"zero\">@string/notecopied_zero</item>\n\t\t<item quantity=\"two\">@string/notecopied_two</item>\n\t\t<item quantity=\"few\">@string/notecopied_few</item>\n\t\t<item quantity=\"many\">@string/notecopied_many</item>\n\t\t<item quantity=\"other\">@string/notecopied_other</item>\n\t</plurals>\n\t<plurals name=\"notedeleted_msg\" translatable=\"false\">\n\t\t<item quantity=\"one\">@string/notedeleted_one</item>\n\t\t<item quantity=\"zero\">@string/notedeleted_zero</item>\n\t\t<item quantity=\"two\">@string/notedeleted_two</item>\n\t\t<item quantity=\"few\">@string/notedeleted_few</item>\n\t\t<item quantity=\"many\">@string/notedeleted_many</item>\n\t\t<item quantity=\"other\">@string/notedeleted_other</item>\n\t</plurals>\n\t<plurals name=\"mode_choose\" translatable=\"false\">\n\t\t<item quantity=\"one\">@string/selected_one</item>\n\t\t<item quantity=\"zero\">@string/selected_zero</item>\n\t\t<item quantity=\"two\">@string/selected_two</item>\n\t\t<item quantity=\"few\">@string/selected_few</item>\n\t\t<item quantity=\"many\">@string/selected_many</item>\n\t\t<item quantity=\"other\">@string/selected_other</item>\n\t</plurals>\n\n\t<string name=\"move\">Move</string>\n\t<string name=\"move_to\">Move to</string>\n\t<string name=\"moved_x_to_list\">Moved %1$d to %2$s</string>\n\n\t<string name=\"account\">Account</string>\n\n\t<string name=\"lock_note\">Lock note</string>\n\t<string name=\"unlock_note\">Unlock note</string>\n\t<string name=\"locked\">Locked</string>\n\t<string name=\"unlocked\">Unlocked</string>\n\t<string name=\"password_required\">Password required</string>\n\t<string name=\"select_account\">Select an account</string>\n\t<string name=\"password\">Password</string>\n\t<string name=\"password_set\">Password set</string>\n\t<string name=\"password_cleared\">Password cleared</string>\n\t<string name=\"passwords_dont_match\">Passwords do not match</string>\n\t<string name=\"password_incorrect\">Password incorrect</string>\n\t<string name=\"enter_password\">Enter password</string>\n\t<string name=\"enter_new_password\">Enter new password</string>\n\t<string name=\"confirm_new_password\">Confirm new password</string>\n\t<string name=\"apply\">Apply</string>\n\t<string name=\"clear_password\">Clear password</string>\n\t<string name=\"password_info\">Any one note can be locked, limiting viewing and modifying it. It remains fully accessible on other devices.</string>\n\t<string name=\"about\">About</string>\n\t<string name=\"sync_failed\">Sync failed</string>\n\t<string name=\"sync_login_failed\">Could not log in to use Google Tasks</string>\n\n\t<string name=\"completed\">Completed</string>\n\t<string name=\"date_header_overdue\">Overdue</string>\n\t<string name=\"date_header_today\">Today</string>\n\t<string name=\"date_header_tomorrow\">Tomorrow</string>\n\t<string name=\"date_header_future\">Later</string>\n\t<string name=\"date_header_none\">No date</string>\n\t<string name=\"date_header_completed\">Completed</string>\n\t<string name=\"next_5_days\">Next 5 days</string>\n\t<string name=\"next_n_days\">Next %d days</string>\n\t<string name=\"this_week\">This week</string>\n\t<string name=\"please_select_note\">Please select or create a note</string>\n\t<string name=\"please_create_note\">Please create a note</string>\n\t<string name=\"hide_checkbox\">Hide checkbox</string>\n\t<string name=\"hide_checkbox_summary_on\">Checkbox hidden</string>\n\t<string name=\"hide_checkbox_summary_off\">Checkbox shown</string>\n\t<string name=\"hide_date\">Hide due date</string>\n\t<string name=\"item_max_height\">Max height of notes/tasks, in rows</string>\n\t<string name=\"long_date_format\">Long-date format, for the panel</string>\n\t<string name=\"short_date_format\">Short-date format, for the list</string>\n\t<string name=\"select_date\">Select date</string>\n\t<string name=\"localedefault\">Device default</string>\n\n\t<!-- settings string keys -->\n\t<string name=\"hide_header\">Hide entire widget header</string>\n\t<string name=\"transparency\">Transparency</string>\n\t<string name=\"notes_shortcut\">Notes shortcut</string>\n\t<string name=\"shortcut_help1\">Makes the shortcut open a new note in the selected list with an open text editor.</string>\n\t<string name=\"loading_widget\">Loading widget…</string>\n\t<string name=\"default_list\">Default list</string>\n\t<string name=\"please_type_before_reminder\">Please type some text before adding a reminder</string>\n\t<string name=\"notecopied_one\">Copied one note</string>\n\t<string name=\"notecopied_other\">Copied %d notes</string>\n\t<string name=\"notecopied_zero\" translatable=\"false\">@string/notecopied_other</string>\n\t<string name=\"notecopied_two\" translatable=\"false\">@string/notecopied_other</string>\n\t<string name=\"notecopied_few\" translatable=\"false\">@string/notecopied_other</string>\n\t<string name=\"notecopied_many\" translatable=\"false\">@string/notecopied_other</string>\n\t<string name=\"notedeleted_one\">Deleted one note</string>\n\t<string name=\"notedeleted_other\">Deleted %d notes</string>\n\t<string name=\"notedeleted_zero\" translatable=\"false\">@string/notedeleted_other</string>\n\t<string name=\"notedeleted_two\" translatable=\"false\">@string/notedeleted_other</string>\n\t<string name=\"notedeleted_few\" translatable=\"false\">@string/notedeleted_other</string>\n\t<string name=\"notedeleted_many\" translatable=\"false\">@string/notedeleted_other</string>\n\t<string name=\"selected_one\">Selected one note</string>\n\t<string name=\"selected_other\">Selected %d notes</string>\n\t<string name=\"selected_zero\" translatable=\"false\">@string/selected_other</string>\n\t<string name=\"selected_two\" translatable=\"false\">@string/selected_other</string>\n\t<string name=\"selected_few\" translatable=\"false\">@string/selected_other</string>\n\t<string name=\"selected_many\" translatable=\"false\">@string/selected_other</string>\n\t<string name=\"background_sync\">Background sync</string>\n\t<string name=\"background_sync_info\">Syncs once per hour</string>\n\t<string name=\"sync_on_change\">Sync on changes</string>\n\t<string name=\"sync_on_change_info\">Schedules a sync shortly after a note has changed</string>\n\t<string name=\"sync_on_start\">Sync on app start</string>\n\t<string name=\"sync_on_start_info\">Limits syncing to once every 5 minutes</string>\n\t<string name=\"snooze\">Snooze</string>\n\t<string name=\"silent\">Silent</string>\n\t<string name=\"sound\">Sound</string>\n\t<string name=\"vibrate\">Vibrate</string>\n\t<string name=\"reminders\">Reminders</string>\n\t<string name=\"standard\">Standard</string>\n\t<string name=\"notification_prio_high\">High: On top, expanded</string>\n\t<string name=\"notification_prio_low\">Low: Hidden, minimized</string>\n\t<string name=\"priority\">Priority</string>\n\t<string name=\"notifications\">Notifications</string>\n\t<string name=\"tasks\">Tasks</string>\n\t<string name=\"all_tasks\">All tasks</string>\n\t<string name=\"dashclock_tasks\" translatable=\"false\">@string/tasks</string>\n\t<string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restrict to tasks in</string>\n\t<string name=\"dashclock_show_overdue_tasks\">Show overdue tasks</string>\n\t<string name=\"dashclock_overdue_tasks_show\">Overdue tasks shown</string>\n\t<string name=\"dashclock_overdue_tasks_hide\">Overdue tasks hidden</string>\n\t<string name=\"dashclock_title_activity_tasks_settings\">Settings</string>\n\t<string name=\"dashclock_pref_header_general\">General</string>\n\t<string name=\"dashclock_due_upper_limit_title\">Limit to tasks due at the latest</string>\n\t<string name=\"dashclock_today\">Today</string>\n\t<string name=\"dashclock_tomorrow\">Tomorrow</string>\n\t<string name=\"dashclock_next7\">Next 7 days</string>\n\t<string name=\"dashclock_anytime\">Anytime</string>\n\t<string name=\"dashclock_will_show_as_many_as_possible\">Show as many as possible</string>\n\t<string name=\"dashclock_will_only_show_the_task_due_first\" translatable=\"false\">@string/dashclock_show_only_the_next_task</string>\n\t<string name=\"dashclock_show_only_the_next_task\">Show only the next task</string>\n\t<string name=\"dashclock_header_shown\">Bold list name</string>\n\t<string name=\"dashclock_first_task_shown\">Bold title of first task</string>\n\t<string name=\"dashclock_display_header\">Display list name</string>\n\t<string name=\"editor\">Editor</string>\n\t<string name=\"bold\">Bold</string>\n\t<string name=\"italic\">Italic</string>\n\t<string name=\"normal\">Normal</string>\n\t<string name=\"title_style\">Title style</string>\n\t<string name=\"title_font\">Title font</string>\n\t<string name=\"body_font\">Body font</string>\n\t<string name=\"clickable_links\">Clickable links</string>\n\t<string name=\"small\">Small</string>\n\t<string name=\"medium\">Medium</string>\n\t<string name=\"large\">Large</string>\n\t<string name=\"text_size\">Text size</string>\n\t<string name=\"text\">Text</string>\n\t<string name=\"add_a_reminder\">Add a reminder</string>\n\t<string name=\"backup\">Backup</string>\n\t<string name=\"backup_import\">Import backup</string>\n\t<string name=\"backup_export\">Export backup</string>\n\t<string name=\"backup_import_msg\">Try to import backup from %1$s? This will clear the current database.</string>\n\t<string name=\"backup_export_msg\">Export all notes to %1$s?</string>\n\t<string name=\"backup_import_success\">Backup imported</string>\n\t<string name=\"backup_file_not_found\">Could not find a backup file named NoNonsenseNotes_Backup.json</string>\n\t<string name=\"backup_import_failed\">Could not read the backup file NoNonsenseNotes_Backup.json</string>\n\t<string name=\"backup_export_success\">Backup exported</string>\n\t<string name=\"backup_export_failed\">Could not write to the backup file</string>\n\t<string name=\"sd_card\">SD card</string>\n\t<string name=\"sd_card_sync\">SD card sync</string>\n\t<string name=\"sd_card_summary\">Tasks are kept the same between the app and the SD card. Deleting the files thus deletes the tasks in the app!</string>\n\t<string name=\"directory\">Folder</string>\n\t<string name=\"directory_summary_msg\">Files are saved in %s \\nwhich you can access with your file manager app. Uninstalling the app will also delete these files: to keep your notes, make a backup</string>\n\t<string name=\"cannot_write_to_directory\">Cannot write to directory</string>\n\t<string name=\"bigger_titles\">Bigger titles</string>\n\t<string name=\"bigger_titles_summary\">The title is the first line</string>\n\t<string name=\"sorting\">Sorting</string>\n\t<string name=\"undo\">Undo</string>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<resources xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- this file is for the custom, smaller components of the themes in themes.xml -->\n\n\t<!-- drawer styles for the 4 themes -->\n\t<style name=\"LeftDrawerStyleDark\">\n\t\t<item name=\"android:background\">@color/googlenow_darkergrey</item>\n\t\t<item name=\"android:dividerHeight\">1px</item>\n\t</style>\n\n\t<style name=\"LeftDrawerStyleBlack\">\n\t\t<item name=\"android:background\">@android:color/black</item>\n\t\t<item name=\"android:dividerHeight\">1px</item>\n\t</style>\n\n\t<style name=\"LeftDrawerStyleLight\">\n\t\t<item name=\"android:background\">@color/googlenow_grey</item>\n\t\t<item name=\"android:dividerHeight\">1px</item>\n\t</style>\n\n\t<!-- common widget styles for all themes -->\n\t<style name=\"FragmentHint\">\n\t\t<item name=\"android:enabled\">false</item>\n\t\t<item name=\"android:fontFamily\">sans-serif-light</item>\n\t\t<item name=\"android:paddingTop\">4dip</item>\n\t\t<item name=\"android:textColor\">?android:attr/textColorSecondary</item>\n\t\t<item name=\"android:textAppearance\">?android:attr/textAppearanceLarge</item>\n\t</style>\n\n\t<style name=\"EditorLayoutStyle\">\n\t\t<item name=\"android:layout_marginTop\">@dimen/editor_vertical_margin</item>\n\t\t<item name=\"android:background\">?attr/editorBackgroundColor</item>\n\t\t<item name=\"android:paddingBottom\">@dimen/editor_vertical_margin_bottom</item>\n\t\t<item name=\"android:paddingLeft\">8dp</item>\n\t\t<item name=\"android:paddingRight\">8dp</item>\n\t</style>\n\n\t<style name=\"EditorTextStyle\">\n\t\t<item name=\"android:fontFamily\">sans-serif</item>\n\t\t<item name=\"android:textAppearance\">?android:attr/textAppearanceMedium</item>\n\t\t<item name=\"android:textColor\">?android:attr/textColorPrimary</item>\n\t\t<item name=\"linkify\">true</item>\n\t\t<item name=\"titleFontFamily\">robotocondensed</item>\n\t\t<item name=\"titleFontStyle\">bold</item>\n\t\t<item name=\"titleRelativeSize\">1.3</item>\n\t</style>\n\n\t<!-- 4 item styles for the note in the list view.\n\tThey are selectors which include the highlight color -->\n\t<style name=\"TaskListItemBlack\">\n\t\t<item name=\"android:background\">@drawable/tasklist_item_blackclassic_bg</item>\n\t</style>\n\n\t<style name=\"TaskListItemDark\">\n\t\t<item name=\"android:background\">@drawable/tasklist_item_darkcard_bg</item>\n\t</style>\n\n\t<style name=\"TaskListItemLight\">\n\t\t<item name=\"android:background\">@drawable/tasklist_item_lightcard_bg</item>\n\t</style>\n\n\t<style name=\"TaskListItemLightClassic\">\n\t\t<item name=\"android:background\">@drawable/tasklist_item_lightclassic_bg</item>\n\t</style>\n\n\t<!-- 2 styles -->\n\t<style name=\"ListMarginStyleCard\">\n\t\t<item name=\"android:paddingLeft\">@dimen/listMarginsPadded</item>\n\t\t<item name=\"android:paddingRight\">@dimen/listMarginsPadded</item>\n\t</style>\n\n\t<style name=\"ListMarginStyleClassic\">\n\t\t<!-- the only peculiarity of the \"classic\" theme is that margins are smaller -->\n\t\t<item name=\"android:paddingLeft\">@dimen/listMargins</item>\n\t\t<item name=\"android:paddingRight\">@dimen/listMargins</item>\n\t</style>\n\n\t<!-- 2 styles -->\n\t<style name=\"ListSpaceDividerStyle\">\n\t\t<item name=\"android:divider\">@android:color/transparent</item>\n\t\t<item name=\"android:dividerHeight\">4dp</item>\n\t</style>\n\n\t<style name=\"ListOnePxDividerStyle\">\n\t\t<item name=\"android:divider\">?android:attr/dividerVertical</item>\n\t\t<item name=\"android:dividerHeight\">1px</item>\n\t</style>\n\n\t<!-- For DashClock Settings -->\n\t<style name=\"DashClockSettings.Theme\" parent=\"@style/Theme.AppCompat.DayNight\">\n\t\t<!-- It is used in the manifest -->\n\t\t<item name=\"colorAccent\">@color/accent</item>\n\t</style>\n\n\t<!-- Used only in weekdays_layout.xml, It sets the appearance of every button\n\t\t in that that strip with MON TUE WED ... in the task detail view -->\n\t<style name=\"GreyableButtonToggle\" parent=\"android:Widget.Material.Button.Toggle\">\n\t\t<item name=\"android:background\">@drawable/btn_toggle_bg</item>\n\t\t<item name=\"android:textAppearance\">?android:attr/textAppearanceSmall</item>\n\t\t<item name=\"android:gravity\">center</item>\n\t\t<item name=\"android:padding\">0dp</item>\n\t</style>\n\n\t<!-- When users increase the text size in the accessibility settings, the search suggestions\n\t popup cuts the text (it only shows the top half of the letters), because the height of the\n\t \"dropdown menu list item\" is fixed in:\n\t androidx.appcompat.R.layout.abc_search_dropdown_item_icons_2line\n\t We solve the bug with this override of the style used by \"dropdown list items\"\n\t  such as the search suggestions -->\n\t<style name=\"ReadableSearchSuggestionListItem\" parent=\"android:Widget.Material.DropDownItem\">\n\t\t<!-- on API versions < 26 the bug is not present\n\t\t and we don't have the API to fix it, anyway -->\n\t</style>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n<resources xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- TODO add a Material You theme.\n         Names of colors you can use (for example <item>?colorPrimary</item>):\n         colorPrimary colorOnPrimary colorPrimaryContainer colorOnPrimaryContainer\n         colorError colorOnError colorErrorContainer colorOnErrorContainer\n         colorOnBackground colorSurface colorOnSurface\n         Problems:\n         * alertdialogs don't show the buttons: they're white (Moto G5, Android 7.0)\n         * the default colors are shades of purple, which look horrible on older devices\n\t -->\n\n\t<!-- a base theme used for the app & the widget. Things for the DARK theme can go here -->\n\t<style name=\"ThemeBaseDark\" parent=\"Theme.AppCompat\">\n\t\t<item name=\"android:background\">@null</item>\n\t\t<item name=\"my_list_selector\">?android:attr/activatedBackgroundIndicator</item>\n\n\t\t<item name=\"img_default_selector\">@drawable/img_default_selector_dark</item>\n\t\t<item name=\"viewpager_bg\">@android:drawable/dark_header</item>\n\t\t<item name=\"labelAddItemTextColor\">@color/labelItemTextColorDark</item>\n\t\t<item name=\"list_item_card_background\">@drawable/card_dark</item>\n\t\t<item name=\"android:actionBarStyle\">@android:style/Widget.Holo.ActionBar.Solid</item>\n\t\t<item name=\"drag_handle_color\">@color/dragHandleColorDark</item>\n\t\t<item name=\"TaskListItem\">@style/TaskListItemDark</item>\n\t\t<item name=\"ListDividerStyle\">@style/ListSpaceDividerStyle</item>\n\t\t<item name=\"ListMarginStyle\">@style/ListMarginStyleClassic</item>\n\t\t<item name=\"LeftDrawerStyle\">@style/LeftDrawerStyleDark</item>\n\t\t<item name=\"android:dropDownItemStyle\">@style/ReadableSearchSuggestionListItem</item>\n\t\t<!-- make all dialogs in the app use this dialog theme.\n\t\t I chose a dark theme consistent with the device's OS design. -->\n\t\t<item name=\"alertDialogTheme\">@style/ThemeNnnDialogDark</item>\n\t\t<item name=\"android:alertDialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"dialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"android:dialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"android:datePickerDialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"android:timePickerDialogTheme\">?alertDialogTheme</item>\n\t\t<!-- white icons for dark themes. All 4 are needed -->\n\t\t<item name=\"iconTint\">@color/white_alpha85</item>\n\t\t<item name=\"android:drawableTint\">?iconTint</item>\n\t\t<item name=\"android:tint\">?iconTint</item>\n\t\t<item name=\"drawableTint\">?iconTint</item>\n\t\t<!-- theme UI controls like checkboxes and text fields  -->\n\t\t<item name=\"colorAccent\">@color/accent</item>\n\t\t<!-- Not setting colorPrimary & colorPrimaryDark because I don't need them -->\n\t</style>\n\n\t<!-- The full black theme -->\n\t<style name=\"ThemeNnnPitchBlack\" parent=\"@style/ThemeNnnDark\">\n\t\t<item name=\"android:windowBackground\">@android:color/black</item>\n\t\t<item name=\"android:colorBackgroundCacheHint\">@android:color/black</item>\n\t\t<item name=\"editorBackgroundColor\">@android:color/black</item>\n\t\t<item name=\"TaskListItem\">@style/TaskListItemBlack</item>\n\t\t<item name=\"ListDividerStyle\">@style/ListOnePxDividerStyle</item>\n\t\t<item name=\"LeftDrawerStyle\">@style/LeftDrawerStyleBlack</item>\n\t\t<!-- The one with the back & home buttons  -->\n\t\t<item name=\"android:navigationBarColor\">@android:color/black</item>\n\t\t<!-- The one with the notification badges -->\n\t\t<item name=\"android:statusBarColor\">@android:color/black</item>\n\t\t<!-- Used ONLY for the actionbar's background color -->\n\t\t<item name=\"colorPrimary\">@android:color/black</item>\n\t</style>\n\n\t<!-- The dark theme -->\n\t<style name=\"ThemeNnnDark\" parent=\"@style/ThemeBaseDark\">\n\t\t<item name=\"android:background\">@null</item>\n\t\t<item name=\"android:windowBackground\">@color/googlenow_darkergrey</item>\n\t\t<item name=\"android:colorBackgroundCacheHint\">@color/googlenow_darkergrey</item>\n\t\t<item name=\"viewpager_bg\">@null</item>\n\t\t<item name=\"editorBackgroundColor\">@drawable/card_dark</item>\n\t\t<item name=\"ListMarginStyle\">@style/ListMarginStyleCard</item>\n\t</style>\n\n\t<!-- The light (white, day) theme -->\n\t<style name=\"ThemeNnnLight\" parent=\"Theme.AppCompat.Light\">\n\t\t<item name=\"android:background\">@null</item>\n\t\t<item name=\"android:windowBackground\">@color/googlenow_grey</item>\n\t\t<item name=\"android:colorBackgroundCacheHint\">@color/googlenow_grey</item>\n\t\t<item name=\"my_list_selector\">?android:attr/activatedBackgroundIndicator</item>\n\t\t<item name=\"editorBackgroundColor\">@drawable/card_light</item>\n\t\t<item name=\"img_default_selector\">@drawable/img_default_selector_light</item>\n\t\t<item name=\"viewpager_bg\">@color/googlenow_grey_trans</item>\n\t\t<item name=\"labelAddItemTextColor\">@color/labelItemTextColorLight</item>\n\t\t<item name=\"list_item_card_background\">@drawable/card_light</item>\n\t\t<item name=\"drag_handle_color\">@color/dragHandleColorLight</item>\n\t\t<item name=\"TaskListItem\">@style/TaskListItemLight</item>\n\t\t<item name=\"ListDividerStyle\">@style/ListSpaceDividerStyle</item>\n\t\t<item name=\"ListMarginStyle\">@style/ListMarginStyleCard</item>\n\t\t<item name=\"LeftDrawerStyle\">@style/LeftDrawerStyleLight</item>\n\t\t<item name=\"colorAccent\">@color/accent</item>\n\t\t<item name=\"android:dropDownItemStyle\">@style/ReadableSearchSuggestionListItem</item>\n\t\t<!-- make all dialogs in the app use this dialog theme.\n\t\t I chose a LIGHT theme consistent with the device's OS design. -->\n\t\t<item name=\"alertDialogTheme\">@style/ThemeNnnDialogLight</item>\n\t\t<item name=\"android:alertDialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"dialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"android:dialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"android:datePickerDialogTheme\">?alertDialogTheme</item>\n\t\t<item name=\"android:timePickerDialogTheme\">?alertDialogTheme</item>\n\t\t<!-- these 4 decide the color of every xml drawable icon (while this \"light\" theme\n\t\t is active). Use\t?colorPrimary\t to inherit from android 13's\n\t\t user set dynamic primary color -->\n\t\t<item name=\"iconTint\">@color/black_alpha85</item>\n\t\t<item name=\"android:drawableTint\">?iconTint</item>\n\t\t<item name=\"android:tint\">?iconTint</item>\n\t\t<item name=\"drawableTint\">?iconTint</item>\n\t</style>\n\n\t<!-- \"classic\" theme: shows a more condensed note list, with smaller separators,\n\t \t by design! That aside, it's 99% similar to the light theme -->\n\t<style name=\"ThemeNnnClassicLight\" parent=\"@style/ThemeNnnLight\">\n\t\t<item name=\"TaskListItem\">@style/TaskListItemLightClassic</item>\n\t\t<item name=\"ListDividerStyle\">@style/ListOnePxDividerStyle</item>\n\t\t<item name=\"ListMarginStyle\">@style/ListMarginStyleClassic</item>\n\t</style>\n\n\t<!-- For the activity where you configure the big widget. It has no theme background,\n\t \tto let user's wallpaper shine through -->\n\t<style name=\"ThemeWidgetConfig\" parent=\"@style/ThemeBaseDark\">\n\t\t<item name=\"android:background\">@null</item>\n\t\t<item name=\"android:colorBackgroundCacheHint\">@null</item>\n\t\t<item name=\"android:windowContentOverlay\">@null</item>\n\t\t<item name=\"android:windowShowWallpaper\">true</item>\n\t\t<item name=\"android:windowBackground\">@android:color/transparent</item>\n\t</style>\n\n\t<style name=\"ThemeNnnDialogDark\" parent=\"@android:style/Theme.DeviceDefault.Dialog\">\n\t\t<!-- TODO this overwrites the user-chosen color in android 13, so you should\n\t\t      make a custom \"themes.xml\" file in values-v31 (?) -->\n\t\t<item name=\"android:colorAccent\">@color/accent</item>\n\t</style>\n\n\t<style name=\"ThemeNnnDialogLight\" parent=\"@android:style/Theme.DeviceDefault.Light.Dialog\">\n\t\t<item name=\"android:colorAccent\">@color/accent</item>\n\t</style>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/widget_params.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<resources xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- Need smaller text for phones -->\n\n\t<style name=\"WidgetItemHeaderStyleDark\">\n\t\t<item name=\"android:textColor\">@android:color/primary_text_dark</item>\n\t\t<item name=\"android:textSize\">12sp</item>\n\t\t<item name=\"android:textStyle\">normal</item>\n\t\t<item name=\"android:textAllCaps\">true</item>\n\t</style>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-af/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notas</string>\n    <string name=\"title_create\">Nuwe nota</string>\n    <string name=\"timemachine\">Tydmasjien</string>\n    <string name=\"time\">Tyd</string>\n    <string name=\"done\">Klaar</string>\n    <string name=\"saved\">Gestoor</string>\n    <string name=\"menu_save_and_add\">Stoor en voeg nota by</string>\n    <string name=\"menu_delete\">Vee uit</string>\n    <string name=\"menu_deletelist\">Vee lys uit</string>\n    <string name=\"menu_sync\">Sinkroniseer</string>\n    <string name=\"menu_share\">Stuur na</string>\n    <string name=\"menu_preferences\">Instellings</string>\n    <string name=\"menu_createlist\">Begin nuwe lys</string>\n    <string name=\"menu_clearcompleted\">Vee voltooide items uit</string>\n    <string name=\"menu_setdefaultlist\">Stel as versteklys</string>\n    <string name=\"new_default_set\">Nuwe verstek stel</string>\n    <string name=\"menu_managelists\">Bewerk lys</string>\n    <string name=\"add_item\">Voeg item by</string>\n    <string name=\"delete_question\">Vee uit?</string>\n    <string name=\"delete_items_message\">Is jy seker jy wil hierdie items uitvee?</string>\n    <string name=\"delete_item_message\">Is jy seker jy wil hierdie item uitvee?</string>\n    <string name=\"delete_list_message\">Is jy seker jy wil hierdie lys uitvee?</string>\n    <string name=\"editor_due_date_hint\">Sperdatum</string>\n    <string name=\"editor_title_hint\">Titel</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Redigeer nota</string>\n    <string name=\"default_style\">Verstek styl</string>\n    <string name=\"show_items_as_tasks\">Wys items as take</string>\n    <string name=\"show_items_as_notes\">Wys items as notas</string>\n    <string name=\"sort_list_default\">Verstek sorteervolgorde</string>\n    <string name=\"sort_list_alphabetical\">Sorteer alfabeties</string>\n    <string name=\"sort_list_due\">Sorteer volgens sperdatum</string>\n    <string name=\"sort_list_updated\">Sorteer volgens mees onlangs gewysig</string>\n    <string name=\"sort_list_manual\">Sorteer handmatig</string>\n    <string name=\"search_description\">Notas en take</string>\n    <string name=\"archive\">Argief</string>\n    <string name=\"restore\">Herstel</string>\n    <string name=\"restore_to\">Herstel na</string>\n    <string name=\"search_hint\">Soek</string>\n    <string name=\"settings_theme\">Huidige styl</string>\n    <string name=\"settings_theme_dialog\">Gebruik styl</string>\n    <string name=\"settings_lang\">Taal</string>\n    <string name=\"settings_summary_theme_black\">Swart</string>\n    <string name=\"settings_summary_theme_dark\">Donker</string>\n    <string name=\"settings_summary_theme_light\">Lig</string>\n    <string name=\"settings_summary_theme_classic\">Klassiek</string>\n    <string name=\"settings_cat_appearance\">Voorkoms</string>\n    <string name=\"preference_preview_text\">Dit is hoe die ingevoerde teks sal vertoon</string>\n    <string name=\"settings_account_title\">Kies \\'n rekening</string>\n    <string name=\"settings_account_summary\">Raak om van rekening te wissel</string>\n    <string name=\"settings_cat_syncing\">Sinkronisasie</string>\n    <string name=\"show_from_all_lists\">Alle lyste</string>\n    <string name=\"lists\">Lyste</string>\n    <string name=\"deleted\">Uitgevee</string>\n    <string name=\"repeat\">Herhaal</string>\n    <string name=\"once\">Eenmaal</string>\n    <string name=\"always\">Altyd</string>\n    <string name=\"permission_read_label\">lees notas en lyste</string>\n    <string name=\"permission_read_desc\">Laat die toepassing toe om jou notas en verwante lyste te lees</string>\n    <string name=\"permission_write_label\">redigeer notas en lyste</string>\n    <string name=\"permission_write_desc\">Laat die toepassing toe om notas en lyste te skep, redigeer en uit te vee</string>\n    <string name=\"settings_list_dialog\">Kies \\'n lys</string>\n    <string name=\"settings_list\">Lys</string>\n    <string name=\"drag_to_timetravel\">Gebruik handvatsel vir tyd-reis</string>\n    <string name=\"import_data_question\">Lees data in?</string>\n    <string name=\"import_started\">Lees data in…</string>\n    <string name=\"imported_result\">%1$d notas in %2$d lyste ingelees</string>\n    <string name=\"import_error\">Iets het skeefgeloop: %s</string>\n    <string name=\"move\">Verskuif</string>\n    <string name=\"move_to\">Verskuif na</string>\n    <string name=\"moved_x_to_list\">Het %1$d na %2$s verskuif</string>\n    <string name=\"lock_note\">Sluit nota toe</string>\n    <string name=\"unlock_note\">Sluit nota oop</string>\n    <string name=\"locked\">Geslote</string>\n    <string name=\"unlocked\">Oop</string>\n    <string name=\"password_required\">Wagwoord word vereis</string>\n    <string name=\"select_account\">Kies \\'n rekening</string>\n    <string name=\"password\">Wagwoord</string>\n    <string name=\"password_set\">Wagwoord gestel</string>\n    <string name=\"password_cleared\">Wagwoord uitgevee</string>\n    <string name=\"passwords_dont_match\">Wagwoorde klop nie</string>\n    <string name=\"password_incorrect\">Verkeerde wagwoord</string>\n    <string name=\"enter_password\">Voer wagwoord in</string>\n    <string name=\"enter_new_password\">Voer nuwe wagwoord in</string>\n    <string name=\"confirm_new_password\">Bevesting nuwe wagwoord</string>\n    <string name=\"apply\">Pas toe</string>\n    <string name=\"clear_password\">Vee wagwoord uit</string>\n    <string name=\"password_info\">Sluit \\'n nota om toegang en redigering te beperk. Notas kan een vir een gesluit word. Let op dat die slot slegs vir hierdie toestel geld: as die nota na ander toestelle versprei word, bly dit ten volle toeganklik op daardie toestelle.</string>\n    <string name=\"about\">Omtrent</string>\n    <string name=\"sync_failed\">Sinkronisasie het gefaal</string>\n    <string name=\"sync_login_failed\">Intekening het gefaal, kon nie met Google Take skakel nie</string>\n    <string name=\"completed\">Voltooid</string>\n    <string name=\"date_header_overdue\">Laat</string>\n    <string name=\"date_header_today\">Vandag</string>\n    <string name=\"date_header_tomorrow\">Môre</string>\n    <string name=\"date_header_none\">Geen datum</string>\n    <string name=\"date_header_completed\">Voltooid</string>\n    <string name=\"next_5_days\">Volgende 5 dae</string>\n    <string name=\"next_n_days\">Volgende %d dae</string>\n    <string name=\"this_week\">Hierdie week</string>\n    <string name=\"please_select_note\">Kies of skep \\'n nota</string>\n    <string name=\"please_create_note\">Skep \\'n nota</string>\n    <string name=\"hide_checkbox\">Versteek kiesblok</string>\n    <string name=\"hide_checkbox_summary_on\">Kiesblok sal nie vertoon nie</string>\n    <string name=\"hide_checkbox_summary_off\">Kiesblok sal vertoon </string>\n    <string name=\"hide_date\">Moenie sperdatum wys nie</string>\n    <string name=\"item_max_height\">Maksimum grootte van notas/take</string>\n    <string name=\"long_date_format\">Lang datum-formaat</string>\n    <string name=\"short_date_format\">Kort datum-formaat</string>\n    <string name=\"select_date\">Kies datum</string>\n    <string name=\"localedefault\">Verstek omgewing</string>\n    <string name=\"hide_header\">Moenie die widget opskrif wys nie</string>\n    <string name=\"transparency\">Deurskynendheid</string>\n    <string name=\"notes_shortcut\">Notas kortpad</string>\n    <string name=\"shortcut_help1\">Indien geselekteer sal die kortpad \\'n nuwe nota in die gekose lys skep, en die teksredigeerder oopmaak. Andersins sal die kortpad die gekose lys oopmaak.</string>\n    <string name=\"loading_widget\">Laai widget…</string>\n    <string name=\"default_list\">Versteklys</string>\n    <string name=\"please_type_before_reminder\">Tik iets voor jy \\'n alarm stel</string>\n    <string name=\"notecopied_one\">1 nota gekopieer</string>\n    <string name=\"notecopied_other\">%d notas gekopieer</string>\n    <string name=\"notedeleted_one\">1 nota uitgevee</string>\n    <string name=\"notedeleted_other\">%d notas uitgevee</string>\n    <string name=\"selected_one\">1 nota gekies</string>\n    <string name=\"selected_other\">%d notas gekies</string>\n    <string name=\"background_sync\">Sinkroniseer in die agtergrond</string>\n    <string name=\"background_sync_info\">Sinkroniseer uurliks</string>\n    <string name=\"sync_on_change\">Sinkroniseer elke verandering</string>\n    <string name=\"sync_on_change_info\">Sinkroniseer binnekort indien \\'n nota verander het</string>\n    <string name=\"sync_on_start\">Sinkroniseer wanneer die app oopmaak</string>\n    <string name=\"sync_on_start_info\">Sinkroniseer meestens een keer elke 5 minute</string>\n    <string name=\"snooze\">Dut nog \\'n bietjie</string>\n    <string name=\"silent\">Stil</string>\n    <string name=\"sound\">Klank</string>\n    <string name=\"vibrate\">Vibreer</string>\n    <string name=\"reminders\">Alarms</string>\n    <string name=\"standard\">Standaard</string>\n    <string name=\"notification_prio_high\">Hoog: bo-aan, vollengte</string>\n    <string name=\"notification_prio_low\">Laag: versteek, kortliks</string>\n    <string name=\"priority\">Prioriteit</string>\n    <string name=\"notifications\">Kennisgewings</string>\n    <string name=\"tasks\">Take</string>\n    <string name=\"all_tasks\">All take</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Slegs take in </string>\n    <string name=\"dashclock_show_overdue_tasks\">Wys laat take</string>\n    <string name=\"dashclock_overdue_tasks_show\">Laat take sal vertoon</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Laat take sal nie vertoon nie</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense Notes Instellings</string>\n    <string name=\"dashclock_pref_header_general\">Algemeen</string>\n    <string name=\"dashclock_due_upper_limit_title\">Wys slegs take wat moet klaarkom teen</string>\n    <string name=\"dashclock_today\">Vandag</string>\n    <string name=\"dashclock_tomorrow\">Môre</string>\n    <string name=\"dashclock_next7\">Volgende 7 dae</string>\n    <string name=\"dashclock_anytime\">Enige tyd</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Sal soveel moontlik wys</string>\n    <string name=\"dashclock_show_only_the_next_task\">Wys net die volgende taak</string>\n    <string name=\"dashclock_header_shown\">Lys-name sal benadruk word</string>\n    <string name=\"dashclock_first_task_shown\">Eerste taak se opskrif sal benadruk word</string>\n    <string name=\"dashclock_display_header\">Wys lys naam</string>\n    <string name=\"editor\">Redigeerder</string>\n    <string name=\"bold\">Benadruk</string>\n    <string name=\"italic\">Skuinsdruk</string>\n    <string name=\"normal\">Normaal</string>\n    <string name=\"title_style\">Opskrif styl</string>\n    <string name=\"title_font\">Opskrif font</string>\n    <string name=\"body_font\">Lyfteks font</string>\n    <string name=\"clickable_links\">Kliekbare skakels</string>\n    <string name=\"small\">Klein</string>\n    <string name=\"large\">Groot</string>\n    <string name=\"text_size\">Teksgrootte</string>\n    <string name=\"text\">Teks</string>\n    <string name=\"add_a_reminder\">Skep \\'n alarm</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Lina</string>\n    <string name=\"title_create\">ملاحظة جديدة</string>\n    <string name=\"menu_save_and_add\">لإضافة وحفظ مذكرة جديدة</string>\n    <string name=\"menu_delete\">حذف</string>\n    <string name=\"menu_deletelist\">حذف القائمة</string>\n    <string name=\"menu_sync\">تحديث</string>\n    <string name=\"menu_share\">مشاركة</string>\n    <string name=\"menu_preferences\">الإعدادات</string>\n    <string name=\"menu_createlist\">إنشاء قائمة</string>\n    <string name=\"menu_clearcompleted\">مسح المهام المُنجَزة</string>\n    <string name=\"menu_setdefaultlist\">جعلها قائمة افتراضية</string>\n    <string name=\"menu_managelists\">إدارة القوائم</string>\n    <string name=\"editor_due_date_hint\">تاريخ الاستحقاق</string>\n    <string name=\"editor_title_hint\">العنوان</string>\n    <string name=\"editor_note_hint\">الملاحظة</string>\n    <string name=\"resolve_edit\">تحرير الملاحظة</string>\n    <string name=\"default_style\">النمط الأساسي</string>\n    <string name=\"show_items_as_tasks\">مهام ظاهرة</string>\n    <string name=\"show_items_as_notes\">إظهار العناصر كملاحظات</string>\n    <string name=\"sort_list_default\">الترتيب الافتراضي</string>\n    <string name=\"sort_list_alphabetical\">أ-ي</string>\n    <string name=\"sort_list_due\">حسب التاريخ</string>\n    <string name=\"sort_list_updated\">آخر تعديل</string>\n    <string name=\"sort_list_manual\">يدوي</string>\n    <string name=\"search_hint\">بحث</string>\n    <string name=\"settings_theme\">السمة الحالية</string>\n    <string name=\"settings_theme_dialog\">استخدام السمة</string>\n    <string name=\"settings_lang\">اللغة</string>\n    <string name=\"settings_summary_theme_black\">أسود</string>\n    <string name=\"settings_summary_theme_dark\">داكن</string>\n    <string name=\"settings_summary_theme_light\">فاتح</string>\n    <string name=\"settings_cat_appearance\">المظهر</string>\n    <string name=\"preference_preview_text\">هكذا سيبدو النص في المحرّر</string>\n    <string name=\"settings_account_title\">اختر حسابًا</string>\n    <string name=\"settings_account_summary\">انقر لتغيير الحساب</string>\n    <string name=\"settings_cat_syncing\">المزامنة</string>\n    <string name=\"show_from_all_lists\">كل القوائم</string>\n    <string name=\"deleted\">حُذِفت</string>\n    <string name=\"permission_read_label\">قراءة الملاحظات والقوائم</string>\n    <string name=\"permission_read_desc\">السماح للتطبيق بقراءة ملاحظاتك والقوائم المتعلقة</string>\n    <string name=\"permission_write_label\">تعديل الملاحظات والقوائم</string>\n    <string name=\"permission_write_desc\">السماح للتطبيق بالإدراج، التحديث وحذف الملاحظات والقوائم</string>\n    <string name=\"settings_list_dialog\">أختر قائمة</string>\n    <string name=\"settings_list\">قائمة</string>\n    <string name=\"lock_note\">قفل ملاحظة</string>\n    <string name=\"unlock_note\">الغاء قفل ملاحظة</string>\n    <string name=\"locked\">مقفل</string>\n    <string name=\"unlocked\">مفتوح</string>\n    <string name=\"password_required\">مطلوب كلمة مرور</string>\n    <string name=\"select_account\">أختر حساب</string>\n    <string name=\"password\">كلمة المرور</string>\n    <string name=\"password_set\">إعداد كلمة المرور</string>\n    <string name=\"password_cleared\">مسح كلمة المرور</string>\n    <string name=\"passwords_dont_match\">كلمات المرور غير متطابقة</string>\n    <string name=\"password_incorrect\">كلمة المرور غير صحيحة</string>\n    <string name=\"enter_password\">أدخل كلمة المرور</string>\n    <string name=\"enter_new_password\">أدخل كلمة مرور جديدة</string>\n    <string name=\"confirm_new_password\">تأكيد كلمة المرور</string>\n    <string name=\"apply\">تطبيق</string>\n    <string name=\"clear_password\">مسح كلمة المرور</string>\n    <string name=\"password_info\">إعداد كلمة المرور سوف يتطلب منك ان تقوم بإدخال كلمة المرور لاظهار اي ملاحظات مقفلة. سوف يقوم فقط بحماية حقل الملاحظة لذا سوف يتم الاطلاع على عنوان الملاحظات وتاريخها. كلمة المرور لاتقوم بتشفير الملاحظة بأي حال ويمكن الاطلاع عليها في بريد جوجل او التقويم اذا نسيت كلمة المرور، قم بالمزامنة مع جوجل ثم اعد تركيب التطبيق.</string>\n    <string name=\"about\">حول</string>\n    <string name=\"sync_failed\">فشلت المزامنة</string>\n    <string name=\"sync_login_failed\">فشل الدخول، لايمكن الاتصال بـ Google Tasks</string>\n    <string name=\"completed\">أكتمل</string>\n    <string name=\"date_header_overdue\">متأخر</string>\n    <string name=\"date_header_today\">اليوم</string>\n    <string name=\"date_header_tomorrow\">الغد</string>\n    <string name=\"date_header_future\">لاحقاً</string>\n    <string name=\"date_header_none\">لا تاريخ</string>\n    <string name=\"date_header_completed\">أكتمل</string>\n    <string name=\"please_select_note\">الرجاء اختيار او انشاء ملاحظة</string>\n    <string name=\"please_create_note\">الرجاء انشاء ملاحظة</string>\n    <string name=\"hide_checkbox\">اخفاء مربع الاختيار</string>\n    <string name=\"hide_checkbox_summary_on\">مربع الاختيار سوف يتم إخفاؤه</string>\n    <string name=\"hide_checkbox_summary_off\">مربع الاختيار سوف يظهر</string>\n    <string name=\"hide_date\">اخفاء تاريخ الاستحقاق</string>\n    <string name=\"item_max_height\">أقصى إرتفاع</string>\n    <string name=\"long_date_format\">تنسيق التاريخ الطويل</string>\n    <string name=\"short_date_format\">تنسيق التاريخ القصير</string>\n    <string name=\"localedefault\">محلي افتراضياً</string>\n    <string name=\"hide_header\">اخفاء رأس القطعة بالكامل</string>\n    <string name=\"shortcut_help1\">اذا تم تحديده، الاختصار سوف يقوم بإنشاء ملاحظة جديدة في القائمة التي تم اختيارها ويتم فتحها في المحرر. اذا لم يتم التحديد، الاختصار سيقوم بفتح القائمة التي تم اختيارها.</string>\n    <string name=\"please_type_before_reminder\">يرجى كتابة كلام قبل إضافة تذكير</string>\n    <string name=\"notecopied_one\">نُسخت ملاحظة واحدة</string>\n    <string name=\"notecopied_other\">نُسخت %d ملاحظات</string>\n    <string name=\"notedeleted_one\">حُذفت ملاحظة واحدة</string>\n    <string name=\"notedeleted_other\">حُذفت %d ملاحظة</string>\n    <string name=\"selected_one\">اخُتيرت ملاحظة واحدة</string>\n    <string name=\"selected_other\">اخُتيرت %d ملاحظة</string>\n    <string name=\"background_sync\">مزامنة في الخلفية</string>\n    <string name=\"background_sync_info\">المزامنة مرة واحدة كل ساعة</string>\n    <string name=\"sync_on_change\">مزامنة عند حصول تغييرات</string>\n    <string name=\"sync_on_change_info\">عمل جدولة للمزامنة عاجلة بعد التغيير في الملاحظة</string>\n    <string name=\"sync_on_start\">مزامنة عند بدأ التطبيق</string>\n    <string name=\"sync_on_start_info\">سوف تبدأ المزامنة فقط عند بدء التطبيق وليس عند متابعة التطبيق</string>\n    <string name=\"snooze\">غفوة</string>\n    <string name=\"silent\">صامت</string>\n    <string name=\"sound\">صوت</string>\n    <string name=\"vibrate\">اهتزاز</string>\n    <string name=\"reminders\">تذكيرات</string>\n    <string name=\"standard\">أساسي</string>\n    <string name=\"notification_prio_high\">عالي: في الاعلى، متمدد</string>\n    <string name=\"notification_prio_low\">منخفض : مخفي، مصغّر</string>\n    <string name=\"priority\">الأولوية</string>\n    <string name=\"notifications\">إعلامات</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">إعدادات ملاحظات NoNonsense</string>\n    <string name=\"dashclock_pref_header_general\">عام</string>\n    <string name=\"dashclock_today\">اليوم</string>\n    <string name=\"dashclock_tomorrow\">غداً</string>\n    <string name=\"dashclock_next7\">الأيام ٧ القادمة</string>\n    <string name=\"dashclock_anytime\">اي وقت</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">سيتم الإظهار بقدر المستطاع</string>\n    <string name=\"dashclock_show_only_the_next_task\">عرض المهمة التالية فقط</string>\n    <string name=\"dashclock_first_task_shown\">سيتم عرض عنوان المهمة الأولى بخط سميك</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-bg/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Бележки</string>\n    <string name=\"title_create\">Нова бележка</string>\n    <string name=\"timemachine\">Машина на времето</string>\n    <string name=\"time\">Време</string>\n    <string name=\"done\">Готово</string>\n    <string name=\"saved\">Запазено</string>\n    <string name=\"menu_save_and_add\">Запазване и отваряне на нова бележка</string>\n    <string name=\"menu_delete\">Изтриване</string>\n    <string name=\"menu_deletelist\">Изтриване на списъка</string>\n    <string name=\"menu_sync\">Опресняване</string>\n    <string name=\"menu_share\">Споделяне</string>\n    <string name=\"menu_preferences\">Настройки</string>\n    <string name=\"menu_createlist\">Създаване на списък</string>\n    <string name=\"menu_clearcompleted\">Изтриване на изпълнените</string>\n    <string name=\"menu_setdefaultlist\">Задаване списък по подразб</string>\n    <string name=\"new_default_set\">Нов набор по подразбиране</string>\n    <string name=\"menu_managelists\">Управление на списъците</string>\n    <string name=\"add_item\">Добавяне на обект</string>\n    <string name=\"delete_question\">Изтриване?</string>\n    <string name=\"delete_items_message\">Изтриване на тези обекти?</string>\n    <string name=\"delete_item_message\">Изтриване на този обект?</string>\n    <string name=\"delete_list_message\">Сигурни ли сте, че искате да изтриете списъка?</string>\n    <string name=\"editor_due_date_hint\">Дата за изпълнение</string>\n    <string name=\"editor_title_hint\">Заглавие</string>\n    <string name=\"editor_note_hint\">Бележка</string>\n    <string name=\"resolve_edit\">Редактиране на бележката</string>\n    <string name=\"default_style\">Стил по подразбиране</string>\n    <string name=\"show_items_as_tasks\">проверяеми задачи</string>\n    <string name=\"show_items_as_notes\">прости бележки</string>\n    <string name=\"sort_list_default\">Подредба по подразбиране</string>\n    <string name=\"sort_list_alphabetical\">Подреди по азбучен ред</string>\n    <string name=\"sort_list_due\">Подреди по дата</string>\n    <string name=\"sort_list_updated\">Последна актуализация</string>\n    <string name=\"sort_list_manual\">Ръчно</string>\n    <string name=\"search_description\">Бележки и задачи</string>\n    <string name=\"archive\">Архив</string>\n    <string name=\"restore\">Връщане</string>\n    <string name=\"restore_to\">Връщане към</string>\n    <string name=\"search_hint\">Търсене</string>\n    <string name=\"settings_theme\">Текуща тема</string>\n    <string name=\"settings_theme_dialog\">Използвана тема</string>\n    <string name=\"settings_lang\">Език</string>\n    <string name=\"settings_summary_theme_black\">Черна</string>\n    <string name=\"settings_summary_theme_dark\">Тъмна</string>\n    <string name=\"settings_summary_theme_light\">Светла</string>\n    <string name=\"settings_summary_theme_classic\">Класическа</string>\n    <string name=\"settings_cat_appearance\">Външен вид</string>\n    <string name=\"preference_preview_text\">Така ще изглежда текстът в текстовия редактор</string>\n    <string name=\"settings_account_title\">Изберете профил</string>\n    <string name=\"settings_account_summary\">От тук изберете профил</string>\n    <string name=\"settings_cat_syncing\">Синхронизиране</string>\n    <string name=\"show_from_all_lists\">Всички списъци</string>\n    <string name=\"lists\">Списъци</string>\n    <string name=\"deleted\">Изтрита</string>\n    <string name=\"repeat\">Повторение</string>\n    <string name=\"once\">Веднъж</string>\n    <string name=\"always\">Винаги</string>\n    <string name=\"permission_read_label\">четене на бележки и списъци</string>\n    <string name=\"permission_read_desc\">Разрешава на приложението да чете вашите бележки и свързаните с тях списъци</string>\n    <string name=\"permission_write_label\">промяна на бележки и списъци</string>\n    <string name=\"permission_write_desc\">Разрешава на приложението да вмъква, актуализира и изтрива бележки и списъци</string>\n    <string name=\"settings_list_dialog\">Изберете списък</string>\n    <string name=\"settings_list\">Списък</string>\n    <string name=\"drag_to_timetravel\">Влачете за промяна на времето</string>\n    <string name=\"import_data_question\">Внасяне на данни?</string>\n    <string name=\"import_started\">Данните се внасят…</string>\n    <string name=\"imported_result\">Внесени %1$d бележки в %2$d списъка</string>\n    <string name=\"import_error\">Нещо се обърка: %s</string>\n    <string name=\"move\">Преместване</string>\n    <string name=\"move_to\">Преместване в</string>\n    <string name=\"moved_x_to_list\">Преместена %1$d в %2$s</string>\n    <string name=\"lock_note\">Заключи бележката</string>\n    <string name=\"unlock_note\">Отключи бележката</string>\n    <string name=\"locked\">Заключена</string>\n    <string name=\"unlocked\">Отключена</string>\n    <string name=\"password_required\">Изисква се парола</string>\n    <string name=\"select_account\">Изберете профил</string>\n    <string name=\"password\">Парола</string>\n    <string name=\"password_set\">Паролата е зададена</string>\n    <string name=\"password_cleared\">Паролата е изчистена</string>\n    <string name=\"passwords_dont_match\">Паролата не съвпада</string>\n    <string name=\"password_incorrect\">Неправилна парола</string>\n    <string name=\"enter_password\">Въведете парола</string>\n    <string name=\"enter_new_password\">Въведете нова парола</string>\n    <string name=\"confirm_new_password\">Потвърдете новата парола</string>\n    <string name=\"apply\">Приложи</string>\n    <string name=\"clear_password\">Изчисти паролата</string>\n    <string name=\"password_info\">Всяка една бележка може да бъде заключена, ограничавайки гледането и модифицирането й. Той остава напълно достъпен на други устройства.</string>\n    <string name=\"about\">За приложението</string>\n    <string name=\"sync_failed\">Синхронизирането не успя</string>\n    <string name=\"sync_login_failed\">Не можахте да влезете, за да използвате Google Задачи</string>\n    <string name=\"completed\">Изпълнена</string>\n    <string name=\"date_header_overdue\">Закъсняла</string>\n    <string name=\"date_header_today\">Днес</string>\n    <string name=\"date_header_tomorrow\">Утре</string>\n    <string name=\"date_header_future\">По-късно</string>\n    <string name=\"date_header_none\">Без дата</string>\n    <string name=\"date_header_completed\">Изпълнена</string>\n    <string name=\"next_5_days\">Следващите 5 дни</string>\n    <string name=\"this_week\">Тази седмица</string>\n    <string name=\"please_select_note\">Изберете или създайте бележка</string>\n    <string name=\"please_create_note\">Създайте бележка</string>\n    <string name=\"hide_checkbox\">Скриване на квадратчето</string>\n    <string name=\"hide_checkbox_summary_on\">Да се скрие</string>\n    <string name=\"hide_checkbox_summary_off\">Да се показва</string>\n    <string name=\"hide_date\">Скриване на датата</string>\n    <string name=\"item_max_height\">Максимална височина</string>\n    <string name=\"long_date_format\">Дълъг формат на датата</string>\n    <string name=\"short_date_format\">Кратък формат на датата</string>\n    <string name=\"select_date\">Изберете дата</string>\n    <string name=\"localedefault\">По подразбиране за локала</string>\n    <string name=\"hide_header\">Скрий целия хедър</string>\n    <string name=\"transparency\">Прозрачност</string>\n    <string name=\"notes_shortcut\">Пряк път</string>\n    <string name=\"shortcut_help1\">Прави прекия път да отвори нова бележка в избрания списък с отворен текстов редактор.</string>\n    <string name=\"loading_widget\">Зареждане на джаджата…</string>\n    <string name=\"default_list\">Списък по подразбиране</string>\n    <string name=\"please_type_before_reminder\">Моля, въведете текст, преди да добавите напомняне</string>\n    <string name=\"notecopied_one\">Копирана е една бележка</string>\n    <string name=\"notecopied_other\">Копирани са %d бележки</string>\n    <string name=\"notedeleted_one\">Изтрита е една бележка</string>\n    <string name=\"notedeleted_other\">Изтрити са %d бележки</string>\n    <string name=\"selected_one\">Избрана е една бележка</string>\n    <string name=\"selected_other\">Избрани са %d бележки</string>\n    <string name=\"background_sync\">Синхр. на заден план</string>\n    <string name=\"background_sync_info\">Синхронизира веднъж на час</string>\n    <string name=\"sync_on_change\">Синхр. при промени</string>\n    <string name=\"sync_on_change_info\">Насрочва синхронизация малко след промяна на бележка</string>\n    <string name=\"sync_on_start\">Синхр. при стартиране</string>\n    <string name=\"sync_on_start_info\">Ограничава синхронизирането до веднъж на всеки 5 минути</string>\n    <string name=\"snooze\">Отлагане</string>\n    <string name=\"silent\">Безшумно</string>\n    <string name=\"sound\">Със звук</string>\n    <string name=\"vibrate\">Вибрация</string>\n    <string name=\"reminders\">Напомняния</string>\n    <string name=\"standard\">Стандартен</string>\n    <string name=\"notification_prio_high\">Висок: на преден план, разширено</string>\n    <string name=\"notification_prio_low\">Нисък: скрито, минимизирано</string>\n    <string name=\"priority\">Приоритет</string>\n    <string name=\"notifications\">Известия</string>\n    <string name=\"tasks\">Задачи</string>\n    <string name=\"all_tasks\">Всички задачи</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Ограничаване до задачите в определен списък</string>\n    <string name=\"dashclock_show_overdue_tasks\">Показване на просрочените задачи</string>\n    <string name=\"dashclock_overdue_tasks_show\">Просрочените задачи ще бъдат показвани</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Просрочените задачи ще бъдат скрити</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Настройки</string>\n    <string name=\"dashclock_pref_header_general\">Общи</string>\n    <string name=\"dashclock_due_upper_limit_title\">Ограничаване до последните задачи</string>\n    <string name=\"dashclock_today\">Днес</string>\n    <string name=\"dashclock_tomorrow\">Утре</string>\n    <string name=\"dashclock_next7\">Следващите 7 дни</string>\n    <string name=\"dashclock_anytime\">По всяко време</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Ще покаже възможно най-много</string>\n    <string name=\"dashclock_show_only_the_next_task\">Покажи само следващата задача</string>\n    <string name=\"dashclock_header_shown\">Името на списъка ще бъде показвано с удебелен шрифт</string>\n    <string name=\"dashclock_first_task_shown\">Името на първата задача ще бъде показвано с удебелен шрифт</string>\n    <string name=\"dashclock_display_header\">Показване на заглавната област</string>\n    <string name=\"battery_optimizations_inactive\">Оптимизациите на батерията са изключени</string>\n    <string name=\"app_about_translations\">Помогнете за превода на приложението на https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"unavailable_chose_directory\">Недостъпен. Моля, първо изберете директория</string>\n    <string name=\"enable_sync_desc\">Активира функциите за синхронизиране и позволява избор на източник за синхронизиране. Резервните копия не са засегнати</string>\n    <string name=\"changelog_online\">Списък на промените (онлайн)</string>\n    <string name=\"canceled_note_locked\">Отменено: бележката е заключена</string>\n    <string name=\"order_notes_by\">Сортирайте бележки по:</string>\n    <string name=\"next_month\">Следващ месец</string>\n    <string name=\"next_year\">Сладваща година</string>\n    <string name=\"welcome_note_title\">Добре дошли!</string>\n    <string name=\"welcome_note_row_2\">Отворете това, за да започнете.</string>\n    <string name=\"welcome_note_row_3\">Това приложение има подробен урок. Можете да го намерите на</string>\n    <string name=\"next_n_days\">Следващи %d дни</string>\n    <string name=\"editor\">Редактор</string>\n    <string name=\"title_style\">Стил на заглавието</string>\n    <string name=\"title_font\">Шрифт на заглавието</string>\n    <string name=\"small\">Малък</string>\n    <string name=\"medium\">Среден</string>\n    <string name=\"backup_import\">Възтановяване резервно копие</string>\n    <string name=\"backup_export\">Изнасяне на резервно копие</string>\n    <string name=\"backup_import_failed\">Не може да се прочете резервният файл NoNonsenseNotes_Backup.json</string>\n    <string name=\"backup_export_success\">Резервното копие е изнесено</string>\n    <string name=\"sd_card\">SD карта</string>\n    <string name=\"directory\">Папка</string>\n    <string name=\"cannot_write_to_directory\">Не може да се пише в директорията</string>\n    <string name=\"bigger_titles\">По-големи заглавия</string>\n    <string name=\"bigger_titles_summary\">Заглавието е първият ред</string>\n    <string name=\"undo\">Отмяна</string>\n    <string name=\"show_elements_as\">Показване на обекти като:</string>\n    <string name=\"version\">Версия: %s</string>\n    <string name=\"select_all\">Избере всички</string>\n    <string name=\"tutorial_online\">Ръководство (онлайн)</string>\n    <string name=\"showcase_tutorial_title\">Чувствате се изгубени?</string>\n    <string name=\"showcase_tutorial_description\">Имаме онлайн урок. Намерете го в настройките</string>\n    <string name=\"choose_backup_folder\">Изберете папка за резервно копие</string>\n    <string name=\"bold\">Удебелен</string>\n    <string name=\"italic\">Курсив</string>\n    <string name=\"normal\">Нормален</string>\n    <string name=\"backup_import_msg\">Опитайте да внесете резервно копие от %1$s? Това ще изчисти текущата база данни.</string>\n    <string name=\"backup_export_msg\">Да се изнесат ли всички бележки в %1$s?</string>\n    <string name=\"backup_export_failed\">Не можа да се запише в резервния файл</string>\n    <string name=\"sd_card_summary\">Задачите се запазват еднакви между приложението и SD картата. Изтриването на файловете по този начин изтрива задачите в приложението!</string>\n    <string name=\"directory_summary_msg\">Файловете се записват в %s\n\\nдо които можете да получите достъп с вашето приложение за управление на файлове. Деинсталирането на приложението също ще изтрие тези файлове: за да запазите бележките си, направете резервно копие</string>\n    <string name=\"notification_channel_name\">Напомняния за бележки</string>\n    <string name=\"notification_channel_description\">Показва съдържанието на бележката в момента на избраното напомняне</string>\n    <string name=\"permission_denied\">Не можете да продължите: Вие отказахте разрешението</string>\n    <string name=\"no_sync_method_chosen\">Не сте избрали метод за синхронизиране</string>\n    <string name=\"file_picker_not_available\">Инструментът за избор на файл не е наличен</string>\n    <string name=\"feature_is_WIP\">Тази функция е W.I.P.</string>\n    <string name=\"app_about_dev_team\">Създаден от Jonas Kalderstam и поддържан от Campello Manuel</string>\n    <string name=\"app_about_git_repo\">Това приложение е свободен софтуер с авторски права, наличен на https://github.com/spacecowboy/NotePad</string>\n    <string name=\"app_about_bugreports\">Докладвайте грешки и проблеми на https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"app_about_donations\">Приемаме дарения. За да подкрепите работата ни, потърсете %1$s в %2$s</string>\n    <string name=\"disable_android_hibernation\">Деактивирайте хибернацията на Android</string>\n    <string name=\"msg_enable_notifications\">Отидете на настройки -&gt; известия, за да активирате известията</string>\n    <string name=\"disable_android_hibernation_desc\">Това е функция, която прави приложението по-малко надеждно. Може да блокира вашите напомняния. Отворете това, потърсете нещо като спиране на активността на приложението, ако не се използва и го изключете</string>\n    <string name=\"msg_hibernation_already_off\">Хибернацията на Android вече е ИЗКЛЮЧЕНА, нямате нужда от това</string>\n    <string name=\"notifications_blocked\">Известията не се виждат, така че няма да виждате напомнянията. Докоснете тук, намерете категорията с разрешения и активирайте известията за това приложение</string>\n    <string name=\"notifications_visibility\">Видимост на известията</string>\n    <string name=\"notifications_enabled\">Известията се виждат</string>\n    <string name=\"unsupported_readonly_file\">Неподдържан файл само за четене: %s</string>\n    <string name=\"show_completed_notes\">Показване на завършените бележки</string>\n    <string name=\"delete_completed_tasks_question\">Изтриване на всички изпълнени задачи?</string>\n    <string name=\"exact_alarms_summary\">Използва повече батерия, за да показва напомняния за известия по по-надежден начин</string>\n    <string name=\"disable_battery_optimizations\">Изключете оптимизациите на батерията</string>\n    <string name=\"battery_optimizations_active\">Включени оптимизации на батерията</string>\n    <string name=\"overwritten_in_newer_systems\">Може да се отмени от определени настройки на канала за уведомяване</string>\n    <string name=\"allow_exact_reminders\">Разрешете точни напомняния</string>\n    <string name=\"allow_exact_reminders_summary\">Отворете страница, за да позволите на това приложение да изпраща напомняния по-надеждно. Необходим е само след Android 12 Snow Cone</string>\n    <string name=\"for_older_devices\">За по-стари Android устройства</string>\n    <string name=\"prefs_improved_reliability\">Предпочитания за подобряване на надеждността</string>\n    <string name=\"notification_channel_settings\">Настройки на канала за известяване</string>\n    <string name=\"not_selected_yet\">Все още не е избрано</string>\n    <string name=\"account\">Профил</string>\n    <string name=\"clickable_links\">Връзки с възможност за кликване</string>\n    <string name=\"large\">Голям</string>\n    <string name=\"text_size\">Размер на текста</string>\n    <string name=\"text\">Текст</string>\n    <string name=\"backup\">Резервно копие</string>\n    <string name=\"sd_card_sync\">Синхронизиране на SD карта</string>\n    <string name=\"sorting\">Сортиране</string>\n    <string name=\"body_font\">Основен шрифт</string>\n    <string name=\"add_a_reminder\">Добавяне на напомняне</string>\n    <string name=\"backup_import_success\">Резервното копие е внесено</string>\n    <string name=\"backup_file_not_found\">Не може да се намери резервният файл с име NoNonsenseNotes_Backup.json</string>\n    <string name=\"navigation_drawer_open\">Отворете чекмеджето за навигация</string>\n    <string name=\"navigation_drawer_close\">Затворете чекмеджето за навигация</string>\n    <string name=\"use_exact_alarms\">Използвайте точни аларми</string>\n    <string name=\"open_channel_settings_description\">Отворете страница, за да редактирате настройките за известия за това приложение. Те отменят всички настройки, избрани другаде</string>\n    <string name=\"libraries_used\">Библиотеки</string>\n    <string name=\"app_about_license\">Лицензиран GPLv3+, https://www.gnu.org/licenses</string>\n    <string name=\"enable_sync\">Активиране на синхронизирането</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ca-rES/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n<resources>\n    <string name=\"title_create\">Nova anotació</string>\n    <string name=\"menu_delete\">Esborra</string>\n    <string name=\"menu_deletelist\">Esborra la llista</string>\n    <string name=\"menu_sync\">Refresca</string>\n    <string name=\"menu_share\">Comparteix</string>\n    <string name=\"menu_preferences\">Configuració</string>\n    <string name=\"menu_createlist\">Crea una nova llista</string>\n    <string name=\"menu_setdefaultlist\">Estableix com a llista per defecte</string>\n    <string name=\"menu_managelists\">Gestiona les llistes</string>\n    <string name=\"editor_due_date_hint\">Data límit</string>\n    <string name=\"editor_title_hint\">Títol</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Editar nota</string>\n    <string name=\"search_hint\">Cerca</string>\n    <string name=\"settings_theme\">Tema actual</string>\n    <string name=\"settings_theme_dialog\">Usa el tema</string>\n    <string name=\"settings_lang\">Idioma</string>\n    <!--\n        Use few if language need it. In English is the same.\n        <item quantity=\"few\">Copied 2 notes.</item>\n    -->\n    <!-- settings string keys -->\n    <!-- \"localtime\" is replaced at runtime with 12/24 hour clock -->\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-co/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name_short\">Note</string>\n    <string name=\"title_create\">Nota nova</string>\n    <string name=\"timemachine\">Cronolugia di a nota</string>\n    <string name=\"time\">Ora</string>\n    <string name=\"done\">Fattu</string>\n    <string name=\"saved\">Arregistratu</string>\n    <string name=\"menu_save_and_add\">Arregistrà è creà una nota nova</string>\n    <string name=\"menu_delete\">Squassà</string>\n    <string name=\"menu_deletelist\">Squassà a lista</string>\n    <string name=\"menu_sync\">Sincrunizà</string>\n    <string name=\"menu_share\">Sparte</string>\n    <string name=\"menu_preferences\">Parametri</string>\n    <string name=\"menu_createlist\">Creà una lista nova</string>\n    <string name=\"menu_clearcompleted\">Squassà e note compie</string>\n    <string name=\"menu_setdefaultlist\">Sceglie cum’è lista predefinita</string>\n    <string name=\"new_default_set\">Nova lista predefinita</string>\n    <string name=\"menu_managelists\">Mudificà e liste</string>\n    <string name=\"add_item\">Aghjunghje un elementu</string>\n    <string name=\"delete_question\">Squassà ?</string>\n    <string name=\"delete_items_message\">Squassà st’elementi ?</string>\n    <string name=\"delete_item_message\">Squassà st’elementu ?</string>\n    <string name=\"delete_list_message\">Squassà sta lista ?</string>\n    <string name=\"delete_completed_tasks_question\">Squassà tutte e tacche compie ?</string>\n    <string name=\"editor_due_date_hint\">Data di scadenza</string>\n    <string name=\"editor_title_hint\">Titulu</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Mudificà a nota</string>\n    <string name=\"default_style\">Stilu predefinitu</string>\n    <string name=\"show_items_as_tasks\">tacche verifichevule</string>\n    <string name=\"show_items_as_notes\">note simplice</string>\n    <string name=\"sort_list_default\">Ordine predefinitu di classificazione</string>\n    <string name=\"sort_list_alphabetical\">Ordine alfabeticu</string>\n    <string name=\"sort_list_due\">Data di scadenza</string>\n    <string name=\"sort_list_updated\">Ultima mudificazione</string>\n    <string name=\"sort_list_manual\">Ordine manuale</string>\n    <string name=\"search_description\">Note è tacche</string>\n    <string name=\"archive\">Archiviu</string>\n    <string name=\"restore\">Risturà</string>\n    <string name=\"restore_to\">Risturà à</string>\n    <string name=\"search_hint\">Ricercà</string>\n    <string name=\"settings_theme\">Tema attuale</string>\n    <string name=\"settings_theme_dialog\">Impiegà un tema</string>\n    <string name=\"settings_lang\">Lingua</string>\n    <string name=\"settings_summary_theme_black\">Neru</string>\n    <string name=\"settings_summary_theme_dark\">Scuru</string>\n    <string name=\"settings_summary_theme_light\">Chjaru</string>\n    <string name=\"settings_summary_theme_classic\">Classicu</string>\n    <string name=\"settings_cat_appearance\">Aspettu</string>\n    <string name=\"preference_preview_text\">Eccu cumu s’affisserà u testu in l’editore</string>\n    <string name=\"settings_account_title\">Selezziunà un contu</string>\n    <string name=\"settings_account_summary\">Picchichjà per cambià di contu</string>\n    <string name=\"settings_cat_syncing\">Sincrunizazione</string>\n    <string name=\"show_from_all_lists\">Tutte e liste</string>\n    <string name=\"lists\">Liste</string>\n    <string name=\"deleted\">Squassata</string>\n    <string name=\"repeat\">Ripete</string>\n    <string name=\"once\">Solu una volta</string>\n    <string name=\"always\">Sempre</string>\n    <string name=\"permission_read_label\">leghje e note è e liste</string>\n    <string name=\"permission_read_desc\">Permette à l’appiecazione di leghje e vostre note è e lsite assuciate</string>\n    <string name=\"permission_write_label\">mudificà e note è e liste</string>\n    <string name=\"permission_write_desc\">Permette à l’appiecazione di creà, mudificà è squassà e note è e liste</string>\n    <string name=\"settings_list_dialog\">Selezziunà una lista</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"drag_to_timetravel\">Trascinà per viaghjà in u tempu</string>\n    <string name=\"navigation_drawer_open\">Apre u listinu di navigazione</string>\n    <string name=\"navigation_drawer_close\">Chjode u listinu di navigazione</string>\n    <string name=\"import_data_question\">Impurtà i dati ?</string>\n    <string name=\"import_started\">Impurtazione di dati…</string>\n    <string name=\"imported_result\">%1$d note in %2$d liste sò state impurtate</string>\n    <string name=\"import_error\">Un sbagliu hè accadutu : %s</string>\n    <string name=\"notification_channel_name\">Ramenti per e note</string>\n    <string name=\"notification_channel_description\">Affissà u cuntenutu di a nota à u mumentu di u ramentu sceltu</string>\n    <string name=\"feature_is_WIP\">Sta funzione hè in corsu di sviluppu</string>\n    <string name=\"permission_denied\">Ùn si pò cuntinuà : avete ricusatu u permessu</string>\n    <string name=\"no_sync_method_chosen\">Ùn avete sceltu una metoda di sincrunizazione</string>\n    <string name=\"file_picker_not_available\">U selettore di schedariu ùn hè micca dispunibule</string>\n    <string name=\"choose_backup_folder\">Sceglie un cartulare per a salvaguardia</string>\n    <string name=\"use_exact_alarms\">Impiegà alarmi esatti</string>\n    <string name=\"exact_alarms_summary\">Què impiegheghja più batteria per affissà e nutificazioni di ramentu in una manera più degnu di fidanza</string>\n    <string name=\"disable_battery_optimizations\">Disattivà i migliuramenti di a batteria</string>\n    <string name=\"battery_optimizations_active\">Migliuramenti di batteria disattivati</string>\n    <string name=\"battery_optimizations_inactive\">Migliuramenti di batteria attivati</string>\n    <string name=\"allow_exact_reminders\">Permette l’alarmi esatti</string>\n    <string name=\"allow_exact_reminders_summary\">Apre una pagina per permette à st’appiecazione di mandà i ramenti d’una manera più degnu di fidanza. Solu richiestu dapoi Android 12 « Snow Cone »</string>\n    <string name=\"for_older_devices\">Per l’apparechji Android più anziani</string>\n    <string name=\"overwritten_in_newer_systems\">Què pò esse supranatu da certi parametri di canale di nutificazione</string>\n    <string name=\"prefs_improved_reliability\">Preferenze per amendà a fidanza</string>\n    <string name=\"notification_channel_settings\">Parametri di canale di nutificazione</string>\n    <string name=\"open_channel_settings_description\">Apre una pagina per mudificà i parametri di nutificazione per st’appiecazione. St’ozzione supranerà tutti i parametri scelti in altrò</string>\n    <string name=\"libraries_used\">Bibliuteche</string>\n    <string name=\"app_about_dev_team\">Creata da Jonas Kalderstam è mantenuta da Campello Manuel</string>\n    <string name=\"app_about_git_repo\">St°appiecazione hè un prugrama liberu è in fonte aperta, dispunibule nant’à https://github.com/spacecowboy/NotePad</string>\n    <string name=\"app_about_license\">Publicatu sottu a licenza GPLv3+, https://www.gnu.org/licenses</string>\n    <string name=\"app_about_bugreports\">Signalà i prublemi è penseri nant’à https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"app_about_translations\">Aiutateci à traduce l’appiecazione nant’à https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"app_about_donations\">Accettemu e dunazioni. Per sustene u nostru travagliu, fighjate %1$s nant’à %2$s</string>\n    <string name=\"disable_android_hibernation\">Disattivà l’invernazione Android</string>\n    <string name=\"disable_android_hibernation_desc\">Ghjè una funzione chì rende l’appiecazione menu degnu di fidanza. Què pò bluccà i vostri ramenti. Aprite què, fighjate qualchì ozzione cum’è « interrompe l’attività di l’appiecazione s’ella hè nonimpiegata » è disattivatela</string>\n    <string name=\"msg_hibernation_already_off\">L’invernazione Android hè dighjà disattivata, ùn ci ne hè bisognu</string>\n    <string name=\"msg_enable_notifications\">Andate a Parametri -&gt; Nutificazioni per attivà e nutificazioni</string>\n    <string name=\"notifications_visibility\">Visibilità di e nutificazioni</string>\n    <string name=\"notifications_enabled\">E nutificazioni sò videvule</string>\n    <string name=\"notifications_blocked\">E nutificazioni ùn sò videvule, tandu ùn viderete micca i ramenti. Picchichjate quì, truvate a categuria di i permessi, è attivate e nutificazioni per st’appiecazione</string>\n    <string name=\"unavailable_chose_directory\">Indispunibule. Prima, ci vole à sceglie un cartulare</string>\n    <string name=\"not_selected_yet\">Ùn hè ancu selezziunatu</string>\n    <string name=\"enable_sync\">Attivà a sincrunizazione</string>\n    <string name=\"enable_sync_desc\">Permette e funzioni di sincrunizazione è di sceglie una fonte cù quelle sincrunizassi. E salvaguardie ùn sò micca tocche</string>\n    <string name=\"changelog_online\">Lista di i cambiamenti (in linea)</string>\n    <string name=\"canceled_note_locked\">Abbandunatu : a nota hè ammarchjunata</string>\n    <string name=\"order_notes_by\">Ordinà e note da :</string>\n    <string name=\"show_elements_as\">Affissà l’elementi cum’è :</string>\n    <string name=\"version\">Versione : %s</string>\n    <string name=\"next_month\">Prossimu mese</string>\n    <string name=\"next_year\">Prossimu annu</string>\n    <string name=\"select_all\">Tuttu selezziunà</string>\n    <string name=\"tutorial_online\">Furmazione autonome (in linea)</string>\n    <string name=\"showcase_tutorial_title\">Vi sentite persi ?</string>\n    <string name=\"showcase_tutorial_description\">Avemu una furmazione autonome in linea. Truvatelu in i parametri</string>\n    <string name=\"welcome_note_title\">Benvenuti !</string>\n    <string name=\"welcome_note_row_2\">Apre què per principià.</string>\n    <string name=\"welcome_note_row_3\">St’appiecazione hà una furmazione autonome detagliata. Si pò fighjalla à a pagina</string>\n    <string name=\"unsupported_readonly_file\">Schedariu in lettura sola micca accettatu : %s</string>\n    <string name=\"show_completed_notes\">Affissà e note compie</string>\n    <string name=\"move\">Dispiazzà</string>\n    <string name=\"move_to\">Dispiazzà versu</string>\n    <string name=\"moved_x_to_list\">%1$d dispiazzatu versu %2$s</string>\n    <string name=\"account\">Contu</string>\n    <string name=\"lock_note\">Ammarchjunà a nota</string>\n    <string name=\"unlock_note\">Spalancà a nota</string>\n    <string name=\"locked\">Ammarchjunata</string>\n    <string name=\"unlocked\">Spalancata</string>\n    <string name=\"password_required\">Parolla d’intesa richiesta</string>\n    <string name=\"select_account\">Selezziunà un contu</string>\n    <string name=\"password\">Parolla d’intesa</string>\n    <string name=\"password_set\">Parolla d’intesa definita</string>\n    <string name=\"password_cleared\">Parolla d’intesa squassata</string>\n    <string name=\"passwords_dont_match\">E parolle d’intesa sò sfarente</string>\n    <string name=\"password_incorrect\">Parolla d’intesa incurretta</string>\n    <string name=\"enter_password\">Stampittà a parolla d’intesa</string>\n    <string name=\"enter_new_password\">Stampittà a parolla d’intesa nova</string>\n    <string name=\"confirm_new_password\">Cunfirmà a parolla d’intesa nova</string>\n    <string name=\"apply\">Appiecà</string>\n    <string name=\"clear_password\">Squassà a parolla d’intesa</string>\n    <string name=\"password_info\">Tutta nota pò esse ammarchjunata, ciò chì limiteghja a so vista è a so mudificazione. A nota resta sana accessibile nant’à l’altri apparechji.</string>\n    <string name=\"about\">Apprupositu</string>\n    <string name=\"sync_failed\">Fiascu di a sincrunizazione</string>\n    <string name=\"sync_login_failed\">Ùn si pò cunnettesi per impiegà Google Tasks</string>\n    <string name=\"completed\">Compiu</string>\n    <string name=\"date_header_overdue\">Scaduta</string>\n    <string name=\"date_header_today\">Oghje</string>\n    <string name=\"date_header_tomorrow\">Dumane</string>\n    <string name=\"date_header_future\">Dopu</string>\n    <string name=\"date_header_none\">Nisuna data</string>\n    <string name=\"date_header_completed\">Compia</string>\n    <string name=\"next_5_days\">I prossimi 5 ghjorni</string>\n    <string name=\"next_n_days\">I prossimi %d ghjorni</string>\n    <string name=\"this_week\">Sta settimana</string>\n    <string name=\"please_select_note\">Ci vole à selezziunà o creà una nota</string>\n    <string name=\"please_create_note\">Ci vole à creà una nota</string>\n    <string name=\"hide_checkbox\">Piattà a casella</string>\n    <string name=\"hide_checkbox_summary_on\">A casella serà piattata</string>\n    <string name=\"hide_checkbox_summary_off\">A casella serà affissata</string>\n    <string name=\"hide_date\">Piattà a data di scadenza</string>\n    <string name=\"item_max_height\">Altezza massima di e note o tacche, in linee</string>\n    <string name=\"long_date_format\">Furmatu longu di data, per u pannellu</string>\n    <string name=\"short_date_format\">Furmatu cortu di data, per a lista</string>\n    <string name=\"select_date\">Selezziunà a data</string>\n    <string name=\"localedefault\">Predefinizione di l’apparechju</string>\n    <string name=\"hide_header\">Piattà l’intestatura sana di l’oggettu graficu</string>\n    <string name=\"transparency\">Trasparenza</string>\n    <string name=\"notes_shortcut\">Accurtatoghju di note</string>\n    <string name=\"shortcut_help1\">S’è l’ozzione hè attiva, l’accurtatoghju aprerà una nota nova in a lista selezziunata cù un editore di testu esternu.</string>\n    <string name=\"loading_widget\">Caricamentu di l’oggettu graficu…</string>\n    <string name=\"default_list\">Lista predefinita</string>\n    <string name=\"please_type_before_reminder\">Ci vole à stampittà qualchì testu prima d’aghjunghje un ramentu</string>\n    <string name=\"notecopied_one\">Una nota cupiata</string>\n    <string name=\"notecopied_other\">%d note cupiate</string>\n    <string name=\"notedeleted_one\">Una nota squassata</string>\n    <string name=\"notedeleted_other\">%d note squassate</string>\n    <string name=\"selected_one\">Una nota selezziunata</string>\n    <string name=\"selected_other\">%d note selezziunate</string>\n    <string name=\"background_sync\">Sincrunizazione in tacca di sfondulu</string>\n    <string name=\"background_sync_info\">Sincrunizà ogni ora</string>\n    <string name=\"sync_on_change\">Sincrunizà à ogni cambiamentu</string>\n    <string name=\"sync_on_change_info\">Pianificheghja una sincrunizazione pocu tempu dopu à un cambiamentu di nota</string>\n    <string name=\"sync_on_start\">Sincrunizà à u lanciu di l’appiecazione</string>\n    <string name=\"sync_on_start_info\">Limiteghja a sincrunizazione à una volta ogni 5 minuti</string>\n    <string name=\"snooze\">Ripete l’alarmu</string>\n    <string name=\"silent\">Silenziosu</string>\n    <string name=\"sound\">Suneria</string>\n    <string name=\"vibrate\">Vibradore</string>\n    <string name=\"reminders\">Ramenti</string>\n    <string name=\"standard\">Nurmale</string>\n    <string name=\"notification_prio_high\">Alta : in altu, allargata</string>\n    <string name=\"notification_prio_low\">Bassa : piattata, minimizata</string>\n    <string name=\"priority\">Priurità</string>\n    <string name=\"notifications\">Nutificazioni</string>\n    <string name=\"tasks\">Tacche</string>\n    <string name=\"all_tasks\">Tutte e tacche</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restringhje e tacche à una lista specificata</string>\n    <string name=\"dashclock_show_overdue_tasks\">Affissà e tacche scadute</string>\n    <string name=\"dashclock_overdue_tasks_show\">E tacche scadute seranu affissate</string>\n    <string name=\"dashclock_overdue_tasks_hide\">E tacche scadute seranu piattate</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Parametri</string>\n    <string name=\"dashclock_pref_header_general\">Generale</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limitu à e tacche scadute à u più tardi</string>\n    <string name=\"dashclock_today\">Oghje</string>\n    <string name=\"dashclock_tomorrow\">Dumane</string>\n    <string name=\"dashclock_next7\">I prossimi 7 ghjorni</string>\n    <string name=\"dashclock_anytime\">Sempre</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Affissà quant’ella si pò</string>\n    <string name=\"dashclock_show_only_the_next_task\">Affissà solu a prossima tacca</string>\n    <string name=\"dashclock_header_shown\">U nome di a lista serà scrittu in grassu</string>\n    <string name=\"dashclock_first_task_shown\">U nome di u titulu di a prima tacca serà scrittu in grassu</string>\n    <string name=\"dashclock_display_header\">Affissà u nome di a lista cum’è intestatura</string>\n    <string name=\"editor\">Editore</string>\n    <string name=\"bold\">Grassu</string>\n    <string name=\"italic\">Cursiva</string>\n    <string name=\"normal\">Nurmale</string>\n    <string name=\"title_style\">Stilu di titulu</string>\n    <string name=\"title_font\">Grafia di u titulu</string>\n    <string name=\"body_font\">Grafia di a nota</string>\n    <string name=\"clickable_links\">Liami clicchevule</string>\n    <string name=\"small\">Chjucu</string>\n    <string name=\"medium\">Medianu</string>\n    <string name=\"large\">Maiò</string>\n    <string name=\"text_size\">Dimensione di u testu</string>\n    <string name=\"text\">Testu</string>\n    <string name=\"add_a_reminder\">Aghjunghje un ramentu</string>\n    <string name=\"backup\">Salvaguardia</string>\n    <string name=\"backup_import\">Impurtà a salvaguardia</string>\n    <string name=\"backup_export\">Espurtà a salvaguardia</string>\n    <string name=\"backup_import_msg\">Pruvà d’impurtà a salvaguardia da %1$s ? Què rimpiazzerà tutte e note di a basa di dati.</string>\n    <string name=\"backup_export_msg\">Espurtà tutte e note versu %1$s ?</string>\n    <string name=\"backup_import_success\">Salvaguardia impurtata bè</string>\n    <string name=\"backup_file_not_found\">Ùn si pò truvà un schedariu di salvaguardia di nome NoNonsenseNotes_Backup.json</string>\n    <string name=\"backup_import_failed\">Ùn si pò leghje u schedariu di salvaguardia NoNonsenseNotes_Backup.json</string>\n    <string name=\"backup_export_success\">Salvaguardia espurtata bè</string>\n    <string name=\"backup_export_failed\">Ùn si pò micca scrive in u schedariu di salvaguardia</string>\n    <string name=\"sd_card\">Carta SD</string>\n    <string name=\"sd_card_sync\">Sincrunizazione nant’à carta SD</string>\n    <string name=\"sd_card_summary\">E tacche sò arregistrate di manera simile trà l’appiecazione è a carta SD. A squassatura di i schedarii squasserà dinù e tacche in l’appiecazione !</string>\n    <string name=\"directory\">Cartulare</string>\n    <string name=\"directory_summary_msg\">I schedarii sò arregistrati in %s \\nChì si pò accede cù a vostra appiecazione di ghjestione di schedarii. A disinstallazione di l’appiecazione squasserà dinù sti schedarii ; per cunservà e vostre note, fate una salvaguardia</string>\n    <string name=\"cannot_write_to_directory\">Ùn si pò micca scrive in u cartulare</string>\n    <string name=\"bigger_titles\">Tituli più maiò</string>\n    <string name=\"bigger_titles_summary\">U titulu hè a prima linea</string>\n    <string name=\"sorting\">Ordine</string>\n    <string name=\"undo\">Disfà</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Poznámky</string>\n    <string name=\"title_create\">Nová poznámka</string>\n    <string name=\"timemachine\">Stroj času</string>\n    <string name=\"time\">Čas</string>\n    <string name=\"done\">Dokončeno</string>\n    <string name=\"saved\">Uloženo</string>\n    <string name=\"menu_save_and_add\">Uložit a přidat poznámku</string>\n    <string name=\"menu_delete\">Odstranit</string>\n    <string name=\"menu_deletelist\">Odstranit seznam</string>\n    <string name=\"menu_sync\">Obnovit</string>\n    <string name=\"menu_share\">Sdílet</string>\n    <string name=\"menu_preferences\">Nastavení</string>\n    <string name=\"menu_createlist\">Vytvořit seznam</string>\n    <string name=\"menu_clearcompleted\">Odstranit dokončené</string>\n    <string name=\"menu_setdefaultlist\">Nastavit jako výchozí seznam</string>\n    <string name=\"new_default_set\">Nové výchozí nastavení</string>\n    <string name=\"menu_managelists\">Upravit seznam</string>\n    <string name=\"add_item\">Přidat položku</string>\n    <string name=\"delete_question\">Odstranit?</string>\n    <string name=\"delete_items_message\">Opravdu odstranit tyto položky?</string>\n    <string name=\"delete_item_message\">Opravdu odstranit tuto položku?</string>\n    <string name=\"delete_list_message\">Opravdu chcete odstranit seznam?</string>\n    <string name=\"editor_due_date_hint\">Termín dokončení</string>\n    <string name=\"editor_title_hint\">Název</string>\n    <string name=\"editor_note_hint\">Poznámka</string>\n    <string name=\"resolve_edit\">Upravit poznámku</string>\n    <string name=\"default_style\">Výchozí styl</string>\n    <string name=\"show_items_as_tasks\">Zobrazit položky jako úkoly</string>\n    <string name=\"show_items_as_notes\">Zobrazit položky jako poznámky</string>\n    <string name=\"sort_list_default\">Výchozí pořadí řazení</string>\n    <string name=\"sort_list_alphabetical\">Seřadit abecedně</string>\n    <string name=\"sort_list_due\">Seřadit podle data uplynutí</string>\n    <string name=\"sort_list_updated\">Seřadit dle poslední úpravy</string>\n    <string name=\"sort_list_manual\">Seřadit manuálně</string>\n    <string name=\"search_description\">Poznámky a úkoly</string>\n    <string name=\"archive\">Archiv</string>\n    <string name=\"restore\">Obnovit</string>\n    <string name=\"restore_to\">Obnovit do</string>\n    <string name=\"search_hint\">Hledat</string>\n    <string name=\"settings_theme\">Aktuální téma</string>\n    <string name=\"settings_theme_dialog\">Zvolte téma</string>\n    <string name=\"settings_lang\">Jazyk</string>\n    <string name=\"settings_summary_theme_black\">Černé</string>\n    <string name=\"settings_summary_theme_dark\">Tmavé</string>\n    <string name=\"settings_summary_theme_light\">Světlé</string>\n    <string name=\"settings_summary_theme_classic\">Klasické</string>\n    <string name=\"settings_cat_appearance\">Vzhled</string>\n    <string name=\"preference_preview_text\">Takhle bude vypadat písmo v editoru</string>\n    <string name=\"settings_account_title\">Zvolte účet</string>\n    <string name=\"settings_account_summary\">Zvolte účet pro synchronizaci</string>\n    <string name=\"settings_cat_syncing\">Synchronizace</string>\n    <string name=\"show_from_all_lists\">Všechny seznamy</string>\n    <string name=\"lists\">Seznamy</string>\n    <string name=\"deleted\">Odstraněno</string>\n    <string name=\"repeat\">Opakovat</string>\n    <string name=\"once\">Jednou</string>\n    <string name=\"always\">Vždy</string>\n    <string name=\"permission_read_label\">číst poznámky a seznamy</string>\n    <string name=\"permission_read_desc\">Povolit aplikaci čtení vašich poznámek a souvisejících seznamů</string>\n    <string name=\"permission_write_label\">upravovat poznámky a seznamy</string>\n    <string name=\"permission_write_desc\">Povolit aplikaci vkládat, upravovat a odstraňovat poznámky a seznamy</string>\n    <string name=\"settings_list_dialog\">Vyberte seznam</string>\n    <string name=\"settings_list\">Seznam</string>\n    <string name=\"drag_to_timetravel\">Táhnout pro posun v čase</string>\n    <string name=\"import_data_question\">Importovat údaje?</string>\n    <string name=\"import_started\">Importuji data…</string>\n    <string name=\"imported_result\">Importováno %1$d poznámek z %2$d seznamů</string>\n    <string name=\"import_error\">Něco se pokazilo: %s</string>\n    <string name=\"move\">Přesunout</string>\n    <string name=\"move_to\">Přesunout do</string>\n    <string name=\"moved_x_to_list\">%1$d přesunuto do %2$s</string>\n    <string name=\"lock_note\">Uzamknout</string>\n    <string name=\"unlock_note\">Odemknout</string>\n    <string name=\"locked\">Uzamčeno</string>\n    <string name=\"unlocked\">Odemčeno</string>\n    <string name=\"password_required\">Vyžadováno heslo</string>\n    <string name=\"select_account\">Zvolte účet</string>\n    <string name=\"password\">Heslo</string>\n    <string name=\"password_set\">Heslo nastaveno</string>\n    <string name=\"password_cleared\">Heslo zrušeno</string>\n    <string name=\"passwords_dont_match\">Hesla se neshodují</string>\n    <string name=\"password_incorrect\">Nesprávné hesli</string>\n    <string name=\"enter_password\">Zadejte heslo</string>\n    <string name=\"enter_new_password\">Zadejte nové heslo</string>\n    <string name=\"confirm_new_password\">Potvrďte nové heslo</string>\n    <string name=\"apply\">Nastavit</string>\n    <string name=\"clear_password\">Zrušit heslo</string>\n    <string name=\"password_info\">Po nastavení hesla bude potřeba pro uzamčené poznámkyzadat před jejich zobrazením heslo. Týká se to jenom textu poznámky, takže bude stálenechráněn nadpis a datum. Nic se nešifruje.</string>\n    <string name=\"about\">O aplikaci</string>\n    <string name=\"sync_failed\">Synchronizace selhala</string>\n    <string name=\"sync_login_failed\">Přihlášení se nezdařilo, nelze se spojit s Google Tasks</string>\n    <string name=\"completed\">Hotovo</string>\n    <string name=\"date_header_overdue\">Po termínu</string>\n    <string name=\"date_header_today\">Dnes</string>\n    <string name=\"date_header_tomorrow\">Zítra</string>\n    <string name=\"date_header_future\">Později</string>\n    <string name=\"date_header_none\">Bez data</string>\n    <string name=\"date_header_completed\">Splněno</string>\n    <string name=\"next_5_days\">Následujích 5 dnů</string>\n    <string name=\"next_n_days\">Následujích %d dnů</string>\n    <string name=\"this_week\">Tento týden</string>\n    <string name=\"please_select_note\">Vyberte nebo vytvořte poznámku</string>\n    <string name=\"please_create_note\">Prosím vytvořte poznámku</string>\n    <string name=\"hide_checkbox\">Skrýt zaškrtávací políčko</string>\n    <string name=\"hide_checkbox_summary_on\">Políčko bude skryté</string>\n    <string name=\"hide_checkbox_summary_off\">Políčko bude zobrazeno</string>\n    <string name=\"hide_date\">Skrýt datum vypršení</string>\n    <string name=\"item_max_height\">Maximální výška poznámek/úkolů</string>\n    <string name=\"long_date_format\">Dlouhý formát data</string>\n    <string name=\"short_date_format\">Krátký formát data</string>\n    <string name=\"select_date\">Vybrat datum</string>\n    <string name=\"localedefault\">Místní nastavení</string>\n    <string name=\"hide_header\">Skrýt celou hlavičku widgetu</string>\n    <string name=\"transparency\">Průhlednost</string>\n    <string name=\"notes_shortcut\">Zkratka poznámek</string>\n    <string name=\"shortcut_help1\">Pokud zaškrtnuto, zkratka vytvoří novou poznámku ve vybraném seznamu a otevře editor. Pokud nezaškrtnuto, zkratka otevře vybraný seznam.</string>\n    <string name=\"loading_widget\">Nahrávání widgetu…</string>\n    <string name=\"default_list\">Výchozí seznam</string>\n    <string name=\"please_type_before_reminder\">Prosím napište nějaký text před nastavením připomínky</string>\n    <string name=\"notecopied_one\">1 poznámka zkopírována</string>\n    <string name=\"notecopied_other\">%d poznámky zkopírovány</string>\n    <string name=\"notedeleted_one\">1 poznámka odstraněna</string>\n    <string name=\"notedeleted_other\">%d poznámky odstraněny</string>\n    <string name=\"selected_one\">1 poznámka vybrána</string>\n    <string name=\"selected_other\">%d poznámky vybrány</string>\n    <string name=\"background_sync\">Synchronizace na pozadí</string>\n    <string name=\"background_sync_info\">Synchronizovat jednou za hodinu</string>\n    <string name=\"sync_on_change\">Synchronizovat při změnách</string>\n    <string name=\"sync_on_change_info\">Naplánuje synchronizaci krátce poté, co se poznámka změní</string>\n    <string name=\"sync_on_start\">Synchronizovat při spuštění aplikace</string>\n    <string name=\"sync_on_start_info\">Bude synchronizovat nejčastěji každých 5 minut</string>\n    <string name=\"snooze\">Odložit</string>\n    <string name=\"silent\">Tichý</string>\n    <string name=\"sound\">Zvuk</string>\n    <string name=\"vibrate\">Vibrovat</string>\n    <string name=\"reminders\">Připomínky</string>\n    <string name=\"standard\">Výchozí</string>\n    <string name=\"notification_prio_high\">Vysoká: Na vrchu, rozšířené</string>\n    <string name=\"notification_prio_low\">Nízká: Skryté, minimalizovat</string>\n    <string name=\"priority\">Priorita</string>\n    <string name=\"notifications\">Upozornění</string>\n    <string name=\"tasks\">Úkoly</string>\n    <string name=\"all_tasks\">Všechny úkoly</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Omezit na úkoly v</string>\n    <string name=\"dashclock_show_overdue_tasks\">Zobrazit zpožděné úkoly</string>\n    <string name=\"dashclock_overdue_tasks_show\">Zpožděné úkoly budou zobrazeny</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Zpožděné úkoly budou skryty</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Nastavení NoNonsense Notes</string>\n    <string name=\"dashclock_pref_header_general\">Obecné</string>\n    <string name=\"dashclock_due_upper_limit_title\">Omezit na úkoly splatné nejpozději</string>\n    <string name=\"dashclock_today\">Dnes</string>\n    <string name=\"dashclock_tomorrow\">Zítra</string>\n    <string name=\"dashclock_next7\">Následujích 7 dnů</string>\n    <string name=\"dashclock_anytime\">Kdykoliv</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Bude zobrazovat co nejvíce</string>\n    <string name=\"dashclock_show_only_the_next_task\">Zobrazit pouze další úkol</string>\n    <string name=\"dashclock_header_shown\">Název seznamu se zobrazí tučně</string>\n    <string name=\"dashclock_first_task_shown\">Titul prvního úkolu se zobrazí tučně</string>\n    <string name=\"dashclock_display_header\">Zobrazit název seznamu</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Tučně</string>\n    <string name=\"italic\">Kurzíva</string>\n    <string name=\"normal\">Normální</string>\n    <string name=\"title_style\">Styl nadpisu</string>\n    <string name=\"title_font\">Písmo nadpisu</string>\n    <string name=\"body_font\">Písmo textu</string>\n    <string name=\"clickable_links\">Klikací odkazy</string>\n    <string name=\"small\">Malé</string>\n    <string name=\"medium\">Střední</string>\n    <string name=\"large\">Velké</string>\n    <string name=\"text_size\">Velikost textu</string>\n    <string name=\"text\">Text</string>\n    <string name=\"add_a_reminder\">Přidat připomínku</string>\n    <string name=\"backup\">Záloha</string>\n    <string name=\"backup_import\">Importovat zálohu</string>\n    <string name=\"backup_export\">Exportovat zálohu</string>\n    <string name=\"backup_import_msg\">Pokusit se o import zálohy z %1$s? Tato akce vymaže aktuální databázi.</string>\n    <string name=\"backup_export_msg\">Exportovat všechny poznámky do %1$s?</string>\n    <string name=\"backup_import_success\">Import zálohy byl úspěšný</string>\n    <string name=\"backup_file_not_found\">Soubor zálohy nebyl nalezen</string>\n    <string name=\"backup_import_failed\">Chyba čtení souboru zálohy</string>\n    <string name=\"backup_export_success\">Zálohování úspěšně dokončeno</string>\n    <string name=\"backup_export_failed\">Nelze zapisovat do souboru zálohy</string>\n    <string name=\"sd_card\">SD karta</string>\n    <string name=\"sd_card_sync\">Synchronizace SD karty</string>\n    <string name=\"sd_card_summary\">Úkoly jsou zrcadleny na SD kartu. Změny se synchronizují oběma směry. Všimněte si, že smazání souborů odstraní úkoly v aplikaci!</string>\n    <string name=\"directory\">Adresář</string>\n    <string name=\"cannot_write_to_directory\">Nelze zapisovat do adresáře</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-da/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Noter</string>\n    <string name=\"title_create\">Ny note</string>\n    <string name=\"timemachine\">Tidsmaskine</string>\n    <string name=\"time\">Tid</string>\n    <string name=\"done\">Udført</string>\n    <string name=\"saved\">Gemt</string>\n    <string name=\"menu_save_and_add\">Gem og åbn en ny note</string>\n    <string name=\"menu_delete\">Slet</string>\n    <string name=\"menu_deletelist\">Slet liste</string>\n    <string name=\"menu_sync\">Opdater</string>\n    <string name=\"menu_share\">Del</string>\n    <string name=\"menu_preferences\">Indstillinger</string>\n    <string name=\"menu_createlist\">Opret liste</string>\n    <string name=\"menu_clearcompleted\">Slet udførte</string>\n    <string name=\"menu_setdefaultlist\">Vælg som standard liste</string>\n    <string name=\"new_default_set\">Nye indstillinger er gemt</string>\n    <string name=\"menu_managelists\">Administrer lister</string>\n    <string name=\"add_item\">Tilføj element</string>\n    <string name=\"delete_question\">Slet?</string>\n    <string name=\"delete_items_message\">Er du sikker på du vil slette disse elementer?</string>\n    <string name=\"delete_item_message\">Er du sikker på du vil slette dette element?</string>\n    <string name=\"delete_list_message\">Er du sikker på du vil slette denne liste?</string>\n    <string name=\"editor_due_date_hint\">Forfaldsdato</string>\n    <string name=\"editor_title_hint\">Titel</string>\n    <string name=\"resolve_edit\">Rediger note</string>\n    <string name=\"default_style\">Standard stil</string>\n    <string name=\"show_items_as_tasks\">Vis elementer som opgaver</string>\n    <string name=\"show_items_as_notes\">Vis elementer som noter</string>\n    <string name=\"sort_list_default\">Standard sorteringsrækkefølge</string>\n    <string name=\"sort_list_alphabetical\">Sorter alfabetisk</string>\n    <string name=\"sort_list_due\">Sorter efter forfaldsdato</string>\n    <string name=\"sort_list_updated\">Sorter efter sidst opdateret</string>\n    <string name=\"sort_list_manual\">Sorter manuelt</string>\n    <string name=\"search_description\">Noter og opgaver</string>\n    <string name=\"archive\">Arkiv</string>\n    <string name=\"restore\">Gendan</string>\n    <string name=\"restore_to\">Gendan til</string>\n    <string name=\"search_hint\">Søg</string>\n    <string name=\"settings_theme\">Nuværende tema</string>\n    <string name=\"settings_theme_dialog\">Brug tema</string>\n    <string name=\"settings_lang\">Sprog</string>\n    <string name=\"settings_summary_theme_black\">Sort</string>\n    <string name=\"settings_summary_theme_dark\">Mørk</string>\n    <string name=\"settings_summary_theme_light\">Lys</string>\n    <string name=\"settings_summary_theme_classic\">Klassisk</string>\n    <string name=\"settings_cat_appearance\">Udseende</string>\n    <string name=\"preference_preview_text\">Sådan vil teksten i editoren blive vist</string>\n    <string name=\"settings_account_title\">Vælg en konto</string>\n    <string name=\"settings_account_summary\">Klik for at ændre konto</string>\n    <string name=\"settings_cat_syncing\">Synkronisering</string>\n    <string name=\"show_from_all_lists\">Alle lister</string>\n    <string name=\"lists\">Lister</string>\n    <string name=\"deleted\">Slettet</string>\n    <string name=\"repeat\">Gentag</string>\n    <string name=\"once\">En gang</string>\n    <string name=\"always\">Altid</string>\n    <string name=\"permission_read_label\">læs noter og lister</string>\n    <string name=\"permission_read_desc\">Tillader applikationen at læse dine noter og relaterede lister</string>\n    <string name=\"permission_write_label\">modificer noter og lister</string>\n    <string name=\"permission_write_desc\">Tillader applikationen at indstætte, opdatere og slette noter og lister</string>\n    <string name=\"settings_list_dialog\">Vælg en liste</string>\n    <string name=\"settings_list\">Liste</string>\n    <string name=\"drag_to_timetravel\">Træk for at rejse i tiden</string>\n    <string name=\"import_data_question\">Importer data?</string>\n    <string name=\"import_started\">Importerer data…</string>\n    <string name=\"imported_result\">Importeret %1$d noter i %2$d lister</string>\n    <string name=\"import_error\">Noget gik galt: %s</string>\n    <string name=\"move\">Flyt</string>\n    <string name=\"move_to\">Flyt til</string>\n    <string name=\"moved_x_to_list\">%1$d flyttet til %2$s</string>\n    <string name=\"lock_note\">Lås note</string>\n    <string name=\"unlock_note\">Oplås note</string>\n    <string name=\"locked\">Låst</string>\n    <string name=\"unlocked\">Ulåst</string>\n    <string name=\"password_required\">Adgangskode påkrævet</string>\n    <string name=\"select_account\">Vælg en konto</string>\n    <string name=\"password\">Adgangskode</string>\n    <string name=\"password_set\">Adgangskode valgt</string>\n    <string name=\"password_cleared\">Adgangskode slettet</string>\n    <string name=\"passwords_dont_match\">Adgangskoder matcher ikke</string>\n    <string name=\"password_incorrect\">Forkert adgangskode</string>\n    <string name=\"enter_password\">Indtast adgangskoden</string>\n    <string name=\"enter_new_password\">Indtast ny adgangskode</string>\n    <string name=\"confirm_new_password\">Bekræft ny adgangskode</string>\n    <string name=\"apply\">Anvend</string>\n    <string name=\"clear_password\">Slet adgangskode</string>\n    <string name=\"password_info\">Angivelse af en adgangskode kræver at du indtaster adgangskoden for at vise alle låste noter. Dette vil kun beskytte note-feltet, så du vil stadig have ubeskyttet adgang til titlen og forfaldsdatoen. Noten vil på ingen måde blive krypteret, så disse kan stadig læses i Google\\'s mail/kalender. Hvis du glemmer din kode, skal du synkronisere med Google og geninstallere app\\'en.</string>\n    <string name=\"about\">Om</string>\n    <string name=\"sync_failed\">Synkornisering fejlet</string>\n    <string name=\"sync_login_failed\">Login fejlet, kunne ikke forbinde til Google Tasks</string>\n    <string name=\"completed\">Udført</string>\n    <string name=\"date_header_overdue\">Forsinket</string>\n    <string name=\"date_header_today\">I dag</string>\n    <string name=\"date_header_tomorrow\">I morgen</string>\n    <string name=\"date_header_future\">Senere</string>\n    <string name=\"date_header_none\">Ingen dato</string>\n    <string name=\"date_header_completed\">Udført</string>\n    <string name=\"next_5_days\">Næste 5 dage</string>\n    <string name=\"next_n_days\">Næste %d dage</string>\n    <string name=\"this_week\">Denne uge</string>\n    <string name=\"please_select_note\">Vælg eller opret venligst en note</string>\n    <string name=\"please_create_note\">Opret venligst en note</string>\n    <string name=\"hide_checkbox\">Skjul afkrydsningsfejlt</string>\n    <string name=\"hide_checkbox_summary_on\">Afkrydsningsfelt vil være skjult</string>\n    <string name=\"hide_checkbox_summary_off\">Afkrydsningsfelt vil være vist</string>\n    <string name=\"hide_date\">Skjul forfaldsdato</string>\n    <string name=\"item_max_height\">Max højde</string>\n    <string name=\"long_date_format\">Langt dato format</string>\n    <string name=\"short_date_format\">Kort dato format</string>\n    <string name=\"select_date\">Vælg dato</string>\n    <string name=\"localedefault\">Lokal standard</string>\n    <string name=\"hide_header\">Skjul hele widget headeren</string>\n    <string name=\"transparency\">Gennemsigtighed</string>\n    <string name=\"notes_shortcut\">Noter genvej</string>\n    <string name=\"shortcut_help1\">Hvis afkrydset, vil genvejen oprette en ny note i den valgte liste og åbne editoren. Hvis ikke afkrydset, vil genvejen åbne den valgte liste.</string>\n    <string name=\"loading_widget\">Indlæser widget…</string>\n    <string name=\"default_list\">Standard liste</string>\n    <string name=\"please_type_before_reminder\">Skriv venligst noget tekst inden du indstiller en påmindelse</string>\n    <string name=\"notecopied_one\">En note kopieret</string>\n    <string name=\"notecopied_other\">%d noter kopieret</string>\n    <string name=\"notedeleted_one\">En note slettet</string>\n    <string name=\"notedeleted_other\">%d noter slettet</string>\n    <string name=\"selected_one\">En note valgt</string>\n    <string name=\"selected_other\">%d noter valgt</string>\n    <string name=\"background_sync\">Baggrundssynkronisering</string>\n    <string name=\"background_sync_info\">Synkroniser en gang i timen</string>\n    <string name=\"sync_on_change\">Synkroniser ved ændringer</string>\n    <string name=\"sync_on_change_info\">Udløser en synkronisering kort efter en note har ændret</string>\n    <string name=\"sync_on_start\">Synkroniser ved app start</string>\n    <string name=\"sync_on_start_info\">Vil kun udløse på en frisk start, ikke ved genoptagelse</string>\n    <string name=\"snooze\">Udsæt</string>\n    <string name=\"silent\">Lydløs</string>\n    <string name=\"sound\">Lyd</string>\n    <string name=\"vibrate\">Vibration</string>\n    <string name=\"reminders\">Påmindelser</string>\n    <string name=\"notification_prio_high\">Høj: Øverst, udvidet</string>\n    <string name=\"notification_prio_low\">Lav: Skjult, minimeret</string>\n    <string name=\"priority\">Prioritet</string>\n    <string name=\"notifications\">Notifikationer</string>\n    <string name=\"tasks\">Opgaver</string>\n    <string name=\"all_tasks\">Alle opgaver</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Begræns til opgaver i en bestemt liste</string>\n    <string name=\"dashclock_show_overdue_tasks\">Vis forfaldne opgaver</string>\n    <string name=\"dashclock_overdue_tasks_show\">Forfaldne opgaver vil blive vist</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Forfaldne opgaver vil blive skjult</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense Notes Indstillinger</string>\n    <string name=\"dashclock_pref_header_general\">Generelt</string>\n    <string name=\"dashclock_due_upper_limit_title\">Begræns til opgaver med forfaldsdato senest</string>\n    <string name=\"dashclock_today\">Idag</string>\n    <string name=\"dashclock_tomorrow\">Imorgen</string>\n    <string name=\"dashclock_next7\">Næste 7 dage</string>\n    <string name=\"dashclock_anytime\">Når som helst</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Vil vise så mange som muligt</string>\n    <string name=\"dashclock_show_only_the_next_task\">Vis kun den næste opgave</string>\n    <string name=\"dashclock_header_shown\">Liste navnet vil blive vist med fed skrift. Hvis \\\"Alle Lister\\\" er valgt, vil \\\"Opgaver\\\" vises</string>\n    <string name=\"dashclock_first_task_shown\">Titlen på første opgave vil blive vist med fed skrift</string>\n    <string name=\"dashclock_display_header\">Vis overskriften</string>\n    <string name=\"bold\">Fed</string>\n    <string name=\"italic\">Kursiv</string>\n    <string name=\"clickable_links\">Klikbare links</string>\n    <string name=\"small\">Lille</string>\n    <string name=\"large\">Stor</string>\n    <string name=\"text_size\">Skriftstørrelse</string>\n    <string name=\"text\">Tekst</string>\n    <string name=\"add_a_reminder\">Tilføj en påmindelse</string>\n    <string name=\"backup_import\">Importer backup</string>\n    <string name=\"backup_export\">Eksporter backup</string>\n    <string name=\"backup_import_msg\">Vil du importere backup fra %1$s? Dette vil slette den nuværende database.</string>\n    <string name=\"backup_export_msg\">Eksporter alle noter til %1$s?</string>\n    <string name=\"backup_import_success\">Backup importeret</string>\n    <string name=\"backup_file_not_found\">Backup-filen kunne ikke findes</string>\n    <string name=\"backup_import_failed\">Kunne ikke læse backup-filen</string>\n    <string name=\"backup_export_success\">Backup eksporteret</string>\n    <string name=\"backup_export_failed\">Kunne ikke skrive til backup-filen</string>\n    <string name=\"sd_card\">SD kort</string>\n    <string name=\"sd_card_sync\">SD kort synkronisering</string>\n    <string name=\"sd_card_summary\">Opgaver er spejlet på SD kortet. Ændringer synkroniseres begge veje. Bemærk at sletning af filerne også vil slette opgaverne i app\\'et!</string>\n    <string name=\"directory\">Mappe</string>\n    <string name=\"cannot_write_to_directory\">Kan ikke skrive til mappe</string>\n    <string name=\"no_sync_method_chosen\">Du har ikke valgt en synkroniseringsmetode</string>\n    <string name=\"choose_backup_folder\">Vælg mappe for sikkerhedskopiering</string>\n    <string name=\"use_exact_alarms\">Eksakte alarmer</string>\n    <string name=\"app_about_translations\">Bistå oversættelsen på https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"file_picker_not_available\">Filvælgeren er ikke tilgængelig</string>\n    <string name=\"allow_exact_reminders\">Tillad eksakte påmindelser</string>\n    <string name=\"undo\">Angre</string>\n    <string name=\"sorting\">Sortering</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notizen</string>\n    <string name=\"title_create\">Neue Notiz</string>\n    <string name=\"timemachine\">Zeitmaschine</string>\n    <string name=\"time\">Uhrzeit</string>\n    <string name=\"done\">Fertig</string>\n    <string name=\"saved\">Gespeichert</string>\n    <string name=\"menu_save_and_add\">Speichern und neue Notiz öffnen</string>\n    <string name=\"menu_delete\">Löschen</string>\n    <string name=\"menu_deletelist\">Liste löschen</string>\n    <string name=\"menu_sync\">Synchronisierung</string>\n    <string name=\"menu_share\">Senden</string>\n    <string name=\"menu_preferences\">Einstellungen</string>\n    <string name=\"menu_createlist\">Liste erstellen</string>\n    <string name=\"menu_clearcompleted\">Erledigte löschen</string>\n    <string name=\"menu_setdefaultlist\">Als Standardliste festlegen</string>\n    <string name=\"new_default_set\">Neuen Standard setzen</string>\n    <string name=\"menu_managelists\">Liste verwalten</string>\n    <string name=\"add_item\">Element hinzufügen</string>\n    <string name=\"delete_question\">Löschen?</string>\n    <string name=\"delete_items_message\">Diese Elemente löschen\\?</string>\n    <string name=\"delete_item_message\">Dieses Element löschen\\?</string>\n    <string name=\"delete_list_message\">Diese Liste löschen\\?</string>\n    <string name=\"editor_due_date_hint\">Fälligkeitsdatum</string>\n    <string name=\"editor_title_hint\">Titel</string>\n    <string name=\"editor_note_hint\">Notiz</string>\n    <string name=\"resolve_edit\">Notiz bearbeiten</string>\n    <string name=\"default_style\">Normaler Style</string>\n    <string name=\"show_items_as_notes\">einfache Notizen</string>\n    <string name=\"sort_list_default\">Standard-Sortierreihenfolge</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_due\">Fälligkeitsdatum</string>\n    <string name=\"sort_list_updated\">Zuletzt aktualisiert</string>\n    <string name=\"sort_list_manual\">Manuell</string>\n    <string name=\"search_description\">Notizen und Aufgaben</string>\n    <string name=\"archive\">Archiv</string>\n    <string name=\"restore\">Wiederherstellen</string>\n    <string name=\"restore_to\">Wiederherstellen in</string>\n    <string name=\"search_hint\">Suche</string>\n    <string name=\"settings_theme\">Aktuelles Design</string>\n    <string name=\"settings_theme_dialog\">Design verwenden</string>\n    <string name=\"settings_lang\">Sprache</string>\n    <string name=\"settings_summary_theme_black\">Schwarz</string>\n    <string name=\"settings_summary_theme_dark\">Dunkel</string>\n    <string name=\"settings_summary_theme_light\">Hell</string>\n    <string name=\"settings_summary_theme_classic\">Klassisch</string>\n    <string name=\"settings_cat_appearance\">Aussehen</string>\n    <string name=\"preference_preview_text\">So wird der Text im Editor dargestellt</string>\n    <string name=\"settings_account_title\">Account wählen</string>\n    <string name=\"settings_account_summary\">Account wechseln</string>\n    <string name=\"settings_cat_syncing\">Wird synchronisiert</string>\n    <string name=\"show_from_all_lists\">Alle Listen</string>\n    <string name=\"lists\">Listen</string>\n    <string name=\"deleted\">Gelöscht</string>\n    <string name=\"repeat\">Wiederholen</string>\n    <string name=\"once\">Einmalig</string>\n    <string name=\"always\">Immer</string>\n    <string name=\"permission_read_label\">Notizen und Listen lesen</string>\n    <string name=\"permission_read_desc\">Erlaubt es der App, Deine Notizen und zugehörigen Listen in No Nonsense Notes zu lesen</string>\n    <string name=\"permission_write_label\">Notizen und Listen bearbeiten</string>\n    <string name=\"permission_write_desc\">Erlaubt der App das Einfügen, Aktualisieren und Löschen von Notizen und Listen in No Nonsense Notes</string>\n    <string name=\"settings_list_dialog\">Wähle eine Liste aus</string>\n    <string name=\"settings_list\">Liste</string>\n    <string name=\"drag_to_timetravel\">Verschieben für Zeitteise</string>\n    <string name=\"import_data_question\">Daten importieren?</string>\n    <string name=\"import_started\">Daten werden importiert…</string>\n    <string name=\"imported_result\">Es wurden %1$d Notizen in %2$d Listen importiert</string>\n    <string name=\"import_error\">Etwas hat nicht funktioniert: %s</string>\n    <string name=\"move\">Verschieben</string>\n    <string name=\"move_to\">Verschieben nach</string>\n    <string name=\"moved_x_to_list\">%1$d nach %2$s verschoben</string>\n    <string name=\"lock_note\">Notiz sperren</string>\n    <string name=\"unlock_note\">Notiz entsperren</string>\n    <string name=\"locked\">Gesperrt</string>\n    <string name=\"unlocked\">Entsperrt</string>\n    <string name=\"password_required\">Passwort benötigt</string>\n    <string name=\"select_account\">Wähle einen Account aus</string>\n    <string name=\"password\">Passwort</string>\n    <string name=\"password_set\">Passwort festgelegt</string>\n    <string name=\"password_cleared\">Passwort gelöscht</string>\n    <string name=\"passwords_dont_match\">Passwort stimmt nicht überein</string>\n    <string name=\"password_incorrect\">Falsches Passwort</string>\n    <string name=\"enter_password\">Passwort eingeben</string>\n    <string name=\"enter_new_password\">Neues Passwort eingeben</string>\n    <string name=\"confirm_new_password\">Neues Passwort bestätigen</string>\n    <string name=\"apply\">Übernehmen</string>\n    <string name=\"clear_password\">Passwort löschen</string>\n    <string name=\"password_info\">Jede einzelne Notiz kann gesperrt werden, so dass sie nur eingeschränkt angezeigt und geändert werden kann. Sie bleibt auf anderen Geräten voll zugänglich.</string>\n    <string name=\"about\">Infos</string>\n    <string name=\"sync_failed\">Synchronisation fehlgeschlagen</string>\n    <string name=\"sync_login_failed\">Konnte mich nicht anmelden, um Google Tasks zu verwenden</string>\n    <string name=\"completed\">Erledigt</string>\n    <string name=\"date_header_overdue\">Überfällig</string>\n    <string name=\"date_header_today\">Heute</string>\n    <string name=\"date_header_tomorrow\">Morgen</string>\n    <string name=\"date_header_future\">Später</string>\n    <string name=\"date_header_none\">Kein Datum</string>\n    <string name=\"date_header_completed\">Erledigt</string>\n    <string name=\"next_5_days\">nächste 5 Tage</string>\n    <string name=\"next_n_days\">nächste %d Tage</string>\n    <string name=\"this_week\">Diese Woche</string>\n    <string name=\"please_select_note\">Bitte wähle oder erstelle eine Notiz</string>\n    <string name=\"please_create_note\">Bitte erstelle eine Notiz</string>\n    <string name=\"hide_checkbox\">Checkbox verbergen </string>\n    <string name=\"hide_checkbox_summary_on\">Kontrollkästchen ausgeblendet</string>\n    <string name=\"hide_checkbox_summary_off\">Kontrollkästchen angezeigt</string>\n    <string name=\"hide_date\">Fälligkeitsdatum verbergen</string>\n    <string name=\"item_max_height\">Maximale Höhe</string>\n    <string name=\"long_date_format\">Langes Datumsformat für das Panel</string>\n    <string name=\"short_date_format\">Kurzes Datumsformat, für die Liste</string>\n    <string name=\"select_date\">Datum wählen</string>\n    <string name=\"localedefault\">Systemsprache</string>\n    <string name=\"hide_header\">Widget-Kopfzeile ausblenden</string>\n    <string name=\"transparency\">Transparenz</string>\n    <string name=\"notes_shortcut\">Notizen Shortcut</string>\n    <string name=\"shortcut_help1\">Die Verknüpfung öffnet eine neue Notiz in der ausgewählten Liste mit einem geöffneten Texteditor.</string>\n    <string name=\"loading_widget\">Widget wird geladen…</string>\n    <string name=\"default_list\">Standard Liste</string>\n    <string name=\"please_type_before_reminder\">Bitte gebe einen Text ein, bevor Du eine Erinnerung hinzufügst</string>\n    <string name=\"notecopied_one\">Eine Notiz kopiert</string>\n    <string name=\"notecopied_other\">%d Notizen kopiert</string>\n    <string name=\"notedeleted_one\">1 Notiz gelöscht</string>\n    <string name=\"notedeleted_other\">%d Notizen gelöscht</string>\n    <string name=\"selected_one\">1 Notiz ausgewählt</string>\n    <string name=\"selected_other\">%d Notizen ausgewählt</string>\n    <string name=\"background_sync\">Im Hintergrund synchronisieren</string>\n    <string name=\"background_sync_info\">Synchronisiert einmal pro Stunde</string>\n    <string name=\"sync_on_change\">Bei Änderungen synchronisieren</string>\n    <string name=\"sync_on_change_info\">Synchronisiert, kurz nachdem eine Notiz geändert wurde</string>\n    <string name=\"sync_on_start\">Beim App-Start synchronisieren</string>\n    <string name=\"sync_on_start_info\">Begrenzt die Synchronisierung auf einmal alle 5 Minuten</string>\n    <string name=\"snooze\">Schlummern</string>\n    <string name=\"silent\">Lautlos</string>\n    <string name=\"sound\">Ton</string>\n    <string name=\"vibrate\">Vibration</string>\n    <string name=\"reminders\">Erinnerungen</string>\n    <string name=\"standard\">Standard</string>\n    <string name=\"notification_prio_high\">Hoch: immer oben, maximiert</string>\n    <string name=\"notification_prio_low\">Niedrig: versteckt, minimiert</string>\n    <string name=\"priority\">Priorität</string>\n    <string name=\"notifications\">Benachrichtigungen</string>\n    <string name=\"tasks\">Aufgaben</string>\n    <string name=\"all_tasks\">Alle Aufgaben</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Auf Aufgaben in bestimmter Liste beschränken</string>\n    <string name=\"dashclock_show_overdue_tasks\">Überfällige Aufgaben anzeigen</string>\n    <string name=\"dashclock_overdue_tasks_show\">Überfällige Aufgaben werden angezeigt</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Überfällige Aufgaben werden ausgeblendet</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Einstellungen</string>\n    <string name=\"dashclock_pref_header_general\">Allgemein</string>\n    <string name=\"dashclock_today\">Heute</string>\n    <string name=\"dashclock_tomorrow\">Morgen</string>\n    <string name=\"dashclock_next7\">Nächste 7 Tage</string>\n    <string name=\"dashclock_anytime\">Irgendwann</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">So viele wie möglich anzeigen</string>\n    <string name=\"dashclock_show_only_the_next_task\">Zeige nur die nächste Aufgabe</string>\n    <string name=\"dashclock_header_shown\">Fettgedruckter Listenname</string>\n    <string name=\"dashclock_first_task_shown\">Fettgedruckter Titel der ersten Aufgabe</string>\n    <string name=\"dashclock_display_header\">Zeige Header</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Fett</string>\n    <string name=\"italic\">Kursiv</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"title_style\">Titel Style</string>\n    <string name=\"title_font\">Titel Schrift</string>\n    <string name=\"body_font\">Schriftart für Textkörper</string>\n    <string name=\"clickable_links\">Klickbare Links </string>\n    <string name=\"small\">Klein</string>\n    <string name=\"medium\">Mittel</string>\n    <string name=\"large\">Groß</string>\n    <string name=\"text_size\">Textgröße</string>\n    <string name=\"text\">Text</string>\n    <string name=\"add_a_reminder\">Erinnerung hinzufügen</string>\n    <string name=\"backup\">Datensicherung</string>\n    <string name=\"backup_import\">Datensicherung importieren</string>\n    <string name=\"backup_export\">Datensicherung Exportieren</string>\n    <string name=\"backup_import_msg\">Soll die Sicherung von %1$s importiert werden? Dies wird die aktuelle Datenbank löschen.</string>\n    <string name=\"backup_export_msg\">Alle Notizen nach %1$s exportieren?</string>\n    <string name=\"backup_import_success\">Sicherung importiert</string>\n    <string name=\"backup_file_not_found\">Sicherungsdatei konnte nicht gefunden werden</string>\n    <string name=\"backup_import_failed\">Sicherungsdatei konnte nicht gelesen werden</string>\n    <string name=\"backup_export_success\">Sicherung exportiert</string>\n    <string name=\"backup_export_failed\">In die Sicherungsdatei konnte nicht geschrieben werden</string>\n    <string name=\"sd_card\">SD-Karte</string>\n    <string name=\"sd_card_sync\">SD-Karten-Synchronisation</string>\n    <string name=\"sd_card_summary\">Die Aufgaben bleiben in der App und auf der SD-Karte gleich. Das Löschen der Dateien löscht also auch die Aufgaben in der App!</string>\n    <string name=\"directory\">Ordner</string>\n    <string name=\"cannot_write_to_directory\">In Ordner kann nicht geschrieben werden</string>\n    <string name=\"disable_android_hibernation\">Android-Ruhezustand deaktivieren</string>\n    <string name=\"notifications_visibility\">Sichtbarkeit von Benachrichtigungen</string>\n    <string name=\"notifications_enabled\">Benachrichtigungen sind sichtbar</string>\n    <string name=\"account\">Konto</string>\n    <string name=\"notification_channel_name\">Erinnerungen für Notizen</string>\n    <string name=\"feature_is_WIP\">Diese Funktion ist noch in Arbeit.</string>\n    <string name=\"notification_channel_settings\">Einstellungen des Benachrichtigungskanals</string>\n    <string name=\"delete_completed_tasks_question\">Alle abgeschlossenen Aufgaben löschen\\?</string>\n    <string name=\"for_older_devices\">Für ältere Android-Geräte</string>\n    <string name=\"app_about_license\">Lizenziert GPLv3+, https://www.gnu.org/licenses</string>\n    <string name=\"not_selected_yet\">Noch nicht ausgewählt</string>\n    <string name=\"no_sync_method_chosen\">Du hast keine Synchronisationsmethode ausgewählt</string>\n    <string name=\"file_picker_not_available\">Die Dateiauswahl ist nicht verfügbar</string>\n    <string name=\"prefs_improved_reliability\">Einstellungen zur Verbesserung der Zuverlässigkeit</string>\n    <string name=\"libraries_used\">Bibliotheken</string>\n    <string name=\"app_about_dev_team\">Erstellt von Jonas Kalderstam und gepflegt von Campello Manuel</string>\n    <string name=\"msg_enable_notifications\">Gehe zu Einstellungen -&gt; Benachrichtigungen, um Benachrichtigungen zu aktivieren</string>\n    <string name=\"dashclock_due_upper_limit_title\">Begrenzung auf spätestens fällige Aufgaben</string>\n    <string name=\"bigger_titles_summary\">Die erste Zeile ist der Titel</string>\n    <string name=\"sorting\">Sortierung</string>\n    <string name=\"undo\">Rückgängig</string>\n    <string name=\"permission_denied\">Kann nicht fortfahren: Du hast die Berechtigung verweigert</string>\n    <string name=\"allow_exact_reminders\">Exakte Erinnerungen erlauben</string>\n    <string name=\"open_channel_settings_description\">Öffne eine Seite, um die Benachrichtigungseinstellungen für diese App zu bearbeiten. Diese Einstellungen haben Vorrang vor allen anderen Einstellungen</string>\n    <string name=\"app_about_translations\">Hilf bei der Übersetzung der App unter https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"notification_channel_description\">Zeigt den Inhalt der Notiz zum Zeitpunkt der gewählten Erinnerung an</string>\n    <string name=\"exact_alarms_summary\">Verbraucht mehr Akku, um Benachrichtigungserinnerungen zuverlässiger anzuzeigen</string>\n    <string name=\"disable_battery_optimizations\">Akku-Optimierungen ausschalten</string>\n    <string name=\"bigger_titles\">Größere Titel</string>\n    <string name=\"use_exact_alarms\">Exakte Alarme verwenden</string>\n    <string name=\"battery_optimizations_active\">Akku-Optimierungen ein</string>\n    <string name=\"battery_optimizations_inactive\">Akku-Optimierungen aus</string>\n    <string name=\"overwritten_in_newer_systems\">Kann durch bestimmte Einstellungen des Benachrichtigungskanals rückgängig gemacht werden</string>\n    <string name=\"allow_exact_reminders_summary\">Öffne eine Seite, damit diese App zuverlässiger Erinnerungen senden kann. Wird erst seit Android 12 Snow Cone benötigt</string>\n    <string name=\"msg_hibernation_already_off\">Der Ruhezustand von Android ist bereits AUS, Du brauchst dies nicht</string>\n    <string name=\"directory_summary_msg\">Dateien werden in %s gespeichert\n\\nauf die Sie mit Ihrer Dateimanager-App zugreifen können. Bei der Deinstallation der App werden auch diese Dateien gelöscht: Erstellen Sie eine Sicherungskopie, um Ihre Notizen zu behalten</string>\n    <string name=\"choose_backup_folder\">Wähle einen Sicherungsordner aus</string>\n    <string name=\"app_about_bugreports\">Melde Fehler und Probleme unter https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"unavailable_chose_directory\">Nicht verfügbar. Bitte wähle zuerst ein Verzeichnis aus</string>\n    <string name=\"notifications_blocked\">Benachrichtigungen sind nicht sichtbar, so dass Du die Erinnerungen nicht siehst. Tippe hier, suche die Kategorie \\\"Berechtigungen\\\" und aktiviere Benachrichtigungen für diese App</string>\n    <string name=\"navigation_drawer_close\">Navigationsschublade schließen</string>\n    <string name=\"navigation_drawer_open\">Navigationsschublade öffnen</string>\n    <string name=\"app_about_git_repo\">Diese App ist freie Software mit Copyleft, erhältlich unter https://github.com/spacecowboy/NotePad</string>\n    <string name=\"changelog_online\">Änderungsprotokoll (online)</string>\n    <string name=\"enable_sync_desc\">Aktiviert die Synchronisierungsfunktionen und ermöglicht die Auswahl einer Synchronisierungsquelle. Sicherungen sind nicht betroffen</string>\n    <string name=\"enable_sync\">Synchronisierung aktivieren</string>\n    <string name=\"show_elements_as\">Elemente anzeigen als:</string>\n    <string name=\"canceled_note_locked\">Abgebrochen: die Notiz ist gesperrt</string>\n    <string name=\"order_notes_by\">Notizen anordnen nach:</string>\n    <string name=\"version\">Version: %s</string>\n    <string name=\"next_year\">Nächstes Jahr</string>\n    <string name=\"next_month\">Nächster Monat</string>\n    <string name=\"show_items_as_tasks\">überprüfbare Aufgabe</string>\n    <string name=\"select_all\">Wähle alle</string>\n    <string name=\"tutorial_online\">Tutorial (online)</string>\n    <string name=\"disable_android_hibernation_desc\">Das ist eine Funktion, die App weniger zuverlässig macht.Es kann Ihre Benachrichtigungen blockieren.Öffnen Sie diese, suchen Sie nach so etwas wie \\\"Suspend App -Aktivität, wenn sie nicht genutzt werden\\\" und schalten Sie es aus</string>\n    <string name=\"welcome_note_title\">Willkommen!</string>\n    <string name=\"welcome_note_row_3\">Diese App hat eine detaillierte Anleitung, zu finden unter</string>\n    <string name=\"app_about_donations\">Wir akzeptieren Spenden. Um unsere Arbeit zu unterstützen schaue nach %1$s in %2$s</string>\n    <string name=\"show_completed_notes\">Abgeschlossene Notizen anzeigen</string>\n    <string name=\"showcase_tutorial_description\">Wir haben ein Online Tutorial. Sie können es in den Einstellungen finden</string>\n    <string name=\"showcase_tutorial_title\">Verwirrt ?</string>\n    <string name=\"welcome_note_row_2\">Zum anfangen das hier öffnen.</string>\n    <string name=\"unsupported_readonly_file\">Nicht unterstützte nur-lesen Datei: %s</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-el/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Σημειώσεις</string>\n    <string name=\"title_create\">Νέα Σημείωση</string>\n    <string name=\"timemachine\">Χρονομηχανή</string>\n    <string name=\"time\">Ώρα</string>\n    <string name=\"done\">Έγινε</string>\n    <string name=\"saved\">Αποθηκεύτηκε</string>\n    <string name=\"menu_save_and_add\">Αποθήκευση και άνοιγμα νέας σημείωσης</string>\n    <string name=\"menu_delete\">Διαγραφή</string>\n    <string name=\"menu_deletelist\">Διαγραφή λίστας</string>\n    <string name=\"menu_sync\">Ανανέωση</string>\n    <string name=\"menu_share\">Μοίρασμα</string>\n    <string name=\"menu_preferences\">Ρυθμίσεις</string>\n    <string name=\"menu_createlist\">Δημιουργίας λίστας</string>\n    <string name=\"menu_clearcompleted\">Καθαρισμός ολοκληρώθηκε</string>\n    <string name=\"menu_setdefaultlist\">Ορισμός ως προεπιλεγμένη λίστα</string>\n    <string name=\"new_default_set\">Νέα προεπιλογή</string>\n    <string name=\"menu_managelists\">Διαχείριση λιστών</string>\n    <string name=\"add_item\">Προσθήκη αντικειμένου</string>\n    <string name=\"delete_question\">Διαγραφή;</string>\n    <string name=\"delete_items_message\">Είστε σίγουρος/η οτι θέλετε να διαγράψετε αυτά τα αντικείμενα;</string>\n    <string name=\"delete_item_message\">Είστε σίγουρος/η οτι θέλετε να διαγράψετε αυτό το αντικείμενο;</string>\n    <string name=\"delete_list_message\">Είστε σίγουρος/η οτι θέλετε να διαγράψετε τη λίστα;</string>\n    <string name=\"editor_due_date_hint\">Ημερομηνία λήξης</string>\n    <string name=\"editor_title_hint\">Τίτλος</string>\n    <string name=\"editor_note_hint\">Σημείωση</string>\n    <string name=\"resolve_edit\">Επεξεργασία Σημείωσης</string>\n    <string name=\"default_style\">Προεπιλεγμένο στύλ</string>\n    <string name=\"show_items_as_tasks\">Προβολή αντικειμένων ως εργασίες</string>\n    <string name=\"show_items_as_notes\">Προβολή αντικειμένων ως σημειώσεις</string>\n    <string name=\"sort_list_default\">Προεπιλεγμένη σειρά ταξινόμησης</string>\n    <string name=\"sort_list_alphabetical\">Ταξινόμηση αλφαβητικά</string>\n    <string name=\"sort_list_due\">Ταξινόμηση κατα ημερομηνία λήξης</string>\n    <string name=\"sort_list_updated\">Ταξινόμηση κατα τελευταία ενημέρωση</string>\n    <string name=\"sort_list_manual\">Χειροκίνητη ταξινόμηση</string>\n    <string name=\"search_description\">Σημειώσεις και εργασίες</string>\n    <string name=\"archive\">Αρχείο</string>\n    <string name=\"restore\">Επαναφορά</string>\n    <string name=\"restore_to\">Επαναφορά σε</string>\n    <string name=\"search_hint\">Αναζήτηση</string>\n    <string name=\"settings_theme\">Τρέχον θέμα</string>\n    <string name=\"settings_theme_dialog\">Χρήση θέματος</string>\n    <string name=\"settings_lang\">Γλώσσα</string>\n    <string name=\"settings_summary_theme_black\">Μαύρο</string>\n    <string name=\"settings_summary_theme_dark\">Σκοτεινό</string>\n    <string name=\"settings_summary_theme_light\">Ανοιχτόχρωμο</string>\n    <string name=\"settings_summary_theme_classic\">Κλασσικό</string>\n    <string name=\"settings_cat_appearance\">Εμφάνιση</string>\n    <string name=\"preference_preview_text\">Έτσι θα εμφανίζεται ο επεξεργαστής  κειμένου</string>\n    <string name=\"settings_account_title\">Επιλογή λογαριασμού</string>\n    <string name=\"settings_account_summary\">Πατήστε για εναλλαγή λογαριασμού</string>\n    <string name=\"settings_cat_syncing\">Συγχρονισμός </string>\n    <string name=\"show_from_all_lists\">Όλες οι λίστες</string>\n    <string name=\"lists\">Λίστες</string>\n    <string name=\"deleted\">Διαγράφηκε</string>\n    <string name=\"repeat\">Επανάληψη</string>\n    <string name=\"once\">Μια φορά</string>\n    <string name=\"always\">Πάντα</string>\n    <string name=\"permission_read_label\">ανάγνωση σημειώσεων και λιστών</string>\n    <string name=\"permission_read_desc\">Επιτρέπει στην εφαρμογή να διαβάζει τις σημειώσεις σας και σχετικές λίστες</string>\n    <string name=\"permission_write_label\">τροποποίηση σημειώσεων και λιστών</string>\n    <string name=\"permission_write_desc\">Επιτρέπει στην εφαρμογή την εισαγωγή, ενημέρωση και διαγραφή σημειώσεων και λιστών</string>\n    <string name=\"settings_list_dialog\">Επιλογή λίστας</string>\n    <string name=\"settings_list\">Λίστα</string>\n    <string name=\"drag_to_timetravel\">Σύρετε για πλοήγηση στο χρόνο</string>\n    <string name=\"import_data_question\">Εισαγωγή δεδομένων;</string>\n    <string name=\"import_started\">Γίνεται εισαγωγή δεδομένων…</string>\n    <string name=\"imported_result\">Εισήχθησαν %1$d σημειώσεις σε %2$d λίστες</string>\n    <string name=\"import_error\">Κάτι πήγε στραβά: %s</string>\n    <string name=\"move\">Μετακίνηση</string>\n    <string name=\"move_to\">Μετακίνηση σε</string>\n    <string name=\"moved_x_to_list\">Μετακινήθηκαν %1$d στο %2$s</string>\n    <string name=\"lock_note\">Κλείδωμα σημείωσης</string>\n    <string name=\"unlock_note\">Ξεκλείδωμα σημείωσης</string>\n    <string name=\"locked\">Κλειδωμένο</string>\n    <string name=\"unlocked\">Ξεκλείδωτο</string>\n    <string name=\"password_required\">Απαιτείται κωδικός</string>\n    <string name=\"select_account\">Επιλογή λογαριασμού</string>\n    <string name=\"password\">Κωδικός</string>\n    <string name=\"password_set\">Ορισμός κωδικού</string>\n    <string name=\"password_cleared\">Κωδικός μηδενίστηκε</string>\n    <string name=\"passwords_dont_match\">Κωδικοί δεν ταιριάζουν</string>\n    <string name=\"password_incorrect\">Λάθος κωδικός</string>\n    <string name=\"enter_password\">Εισάγετε κωδικό</string>\n    <string name=\"enter_new_password\">Εισάγετε νέο κωδικό</string>\n    <string name=\"confirm_new_password\">Επιβεβαίωση νέου κωδικού</string>\n    <string name=\"apply\">Εφαρμογή</string>\n    <string name=\"clear_password\">Μηδενισμός κωδικού</string>\n    <string name=\"password_info\">Κλειδώνοντας μια σημείωση περιορίζεται η πρόσβαση στην ανάγνωση ή τροποποίησή της. Οι σημειώσεις προστατεύονται μόνο εδώ ενώ είναι πλήρως προσβάσιμες από άλλες συσκευές.</string>\n    <string name=\"about\">Σχετικά με την εφαρμογή</string>\n    <string name=\"sync_failed\">Αποτυχία συγχρονισμού</string>\n    <string name=\"sync_login_failed\">Σύνδεση απέτυχε, δεν μπορούσε να συνδεθεί με το Google Tasks</string>\n    <string name=\"completed\">Ολοκληρωμένο</string>\n    <string name=\"date_header_overdue\">Εκπρόθεσμα</string>\n    <string name=\"date_header_today\">Σήμερα</string>\n    <string name=\"date_header_tomorrow\">Αύριο</string>\n    <string name=\"date_header_future\">Αργότερα</string>\n    <string name=\"date_header_none\">Χωρίς ημερομηνία</string>\n    <string name=\"date_header_completed\">Ολοκληρωμένο</string>\n    <string name=\"next_5_days\">Επόμενες 5 ημέρες</string>\n    <string name=\"next_n_days\">Επόμενες %d ημέρες</string>\n    <string name=\"this_week\">Τρέχουσα Εβδομάδα</string>\n    <string name=\"please_select_note\">Παρακαλώ επιλέξτε ή δημιουργήστε μια σημείωση</string>\n    <string name=\"please_create_note\">Παρακαλώ δημιουργήστε μια σημείωση</string>\n    <string name=\"hide_checkbox\">Απόκρυψη πλαισίου ελέγχου</string>\n    <string name=\"hide_checkbox_summary_on\">Το πλαίσιο ελέγχου δεν θα εμφανίζεται</string>\n    <string name=\"hide_checkbox_summary_off\">Το πλαίσιο ελέγχου θα εμφανίζεται</string>\n    <string name=\"hide_date\">Απόκρυψη Ημ/νίας λήξης</string>\n    <string name=\"item_max_height\">Μέγιστο ύψος των σημειώσεων/εργασιών</string>\n    <string name=\"long_date_format\">Μορφή πλήρους ημερομηνίας</string>\n    <string name=\"short_date_format\">Μορφή σύντομης ημερομηνίας</string>\n    <string name=\"select_date\">Επιλέξτε ημερομηνία</string>\n    <string name=\"localedefault\">Προεπιλεγμένη γλώσσα</string>\n    <!-- settings string keys -->\n    <string name=\"hide_header\">Απόκρυψη όλης της επικεφαλίδας widget</string>\n    <string name=\"transparency\">Διαφάνεια</string>\n    <string name=\"notes_shortcut\">Συντόμευση σημειώσεων</string>\n    <string name=\"shortcut_help1\">Εάν επιλεγεί, η συντόμευση θα δημιουργήσει μια νέα σημείωση στην επιλεγμένη λίστα και θα ανοίξει το πρόγραμμα επεξεργασίας. Εάν δεν είναι επιλεγμένη, η συντόμευση θα ανοίξει την επιλεγμένη λίστα.</string>\n    <string name=\"loading_widget\">Φόρτωση Widget…</string>\n    <string name=\"default_list\">Προεπιλεγμένη λίστα</string>\n    <string name=\"please_type_before_reminder\">Παρακαλώ πληκτρολογήστε κάποιο κείμενο πριν να ορίσετε μια υπενθύμιση</string>\n    <string name=\"notecopied_one\">Αντιγραφή 1 σημείωσης</string>\n    <string name=\"notecopied_other\">Αντιγραφή %d σημειώσεων</string>\n    <string name=\"notedeleted_one\">Διαγραφή 1 σημείωσης</string>\n    <string name=\"notedeleted_other\">Διαγράφηκαν %d σημειώσεις</string>\n    <string name=\"selected_one\">Επιλέχτηκε 1 σημείωση</string>\n    <string name=\"selected_other\">Επιλέχτηκαν %d σημειώσεις</string>\n    <string name=\"background_sync\">Συγχρονισμός στο παρασκήνιο</string>\n    <string name=\"background_sync_info\">Συγχρονίζεται κάθε μία ώρα</string>\n    <string name=\"sync_on_change\">Συγχρονισμός σε κάθε αλλαγή</string>\n    <string name=\"sync_on_change_info\">Διενεργεί ένα συγχρονισμό αμέσως μετά από την τροποποίηση μιας σημείωσης</string>\n    <string name=\"sync_on_start\">Συγχρονισμός κατά την έναρξη της εφαρμογής</string>\n    <string name=\"sync_on_start_info\">Θα συγχρονίζεται το πολύ κάθε 5 λεπτά</string>\n    <string name=\"snooze\">Αναβολή</string>\n    <string name=\"silent\">Αθόρυβο</string>\n    <string name=\"sound\">Ήχος</string>\n    <string name=\"vibrate\">Δόνηση</string>\n    <string name=\"reminders\">Υπενθυμίσεις</string>\n    <string name=\"standard\">Τυπικό</string>\n    <string name=\"notification_prio_high\">Υψηλή: Στην κορυφή, σε επέκταση</string>\n    <string name=\"notification_prio_low\">Χαμηλή: Κρυφή, ελαχιστοποιημένη</string>\n    <string name=\"priority\">Προτεραιότητα</string>\n    <string name=\"notifications\">Ειδοποιήσεις</string>\n    <string name=\"tasks\">Εργασίες</string>\n    <string name=\"all_tasks\">Όλες οι εργασίες</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Περιορισμός σε εργασίες στο</string>\n    <string name=\"dashclock_show_overdue_tasks\">Εμφάνιση εκπρόθεσμων εργασιών</string>\n    <string name=\"dashclock_overdue_tasks_show\">Οι εκπρόθεσμες εργασίες θα εμφανίζονται</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Οι εκπρόθεσμες εργασίες δεν θα εμφανίζονται</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Ρυθμίσεις NoNonsense Notes</string>\n    <string name=\"dashclock_pref_header_general\">Γενικές</string>\n    <string name=\"dashclock_due_upper_limit_title\">Εργασίες που λήγουν το αργότερο όριο σε</string>\n    <string name=\"dashclock_today\">Σήμερα</string>\n    <string name=\"dashclock_tomorrow\">Αύριο</string>\n    <string name=\"dashclock_next7\">Επόμενες 7 ημέρες</string>\n    <string name=\"dashclock_anytime\">Οποτεδήποτε</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Θα εμφανίζονται όσο το δυνατόν περισσότερα</string>\n    <string name=\"dashclock_show_only_the_next_task\">Εμφάνισε μόνο την επόμενη εργασία</string>\n    <string name=\"dashclock_header_shown\">Το όνομα της λίστα θα εμφανίζεται με έντονη γραφή</string>\n    <string name=\"dashclock_first_task_shown\">Ο τίτλος της πρώτης εργασίας θα εμφανίζεται με έντονους χαρακτήρες</string>\n    <string name=\"dashclock_display_header\">Εμφανιζόμενο όνομα λίστας</string>\n    <string name=\"editor\">Πρόγραμμα επεξεργασίας</string>\n    <string name=\"bold\">Έντονα</string>\n    <string name=\"italic\">Πλάγια γραφή</string>\n    <string name=\"normal\">Κανονικά</string>\n    <string name=\"title_style\">Στυλ τίτλου</string>\n    <string name=\"title_font\">Γραμματοσειρά τίτλου</string>\n    <string name=\"body_font\">Γραμματοσειρά κυρίως κειμένου</string>\n    <string name=\"clickable_links\">Ενεργοί σύνδεσμοι</string>\n    <string name=\"small\">Μικρά</string>\n    <string name=\"medium\">Μεσαία</string>\n    <string name=\"large\">Μεγάλα</string>\n    <string name=\"text_size\">Μέγεθος κειμένου</string>\n    <string name=\"text\">Κείμενο</string>\n    <string name=\"add_a_reminder\">Προσθήκη υπενθύμισης</string>\n    <string name=\"backup\">Αντίγραφα ασφαλείας</string>\n    <string name=\"backup_import\">Εισαγωγή αντιγράφου ασφαλείας</string>\n    <string name=\"backup_export\">Εξαγωγή αντιγράφου ασφαλείας</string>\n    <string name=\"backup_import_msg\">Επιχειρήσατε να εισαγάγετε αντίγραφο ασφαλείας από %1$s; Αυτό θα διαγράψει την τρέχουσα βάση δεδομένων.</string>\n    <string name=\"backup_export_msg\">Εξαγωγή όλων των σημειώσεων στο %1$s;</string>\n    <string name=\"backup_import_success\">Η εισαγωγή αντιγράφου ασφαλείας ολοκληρώθηκε με επιτυχία</string>\n    <string name=\"backup_file_not_found\">Δεν ήταν δυνατή η εύρεση του αρχείου αντιγράφου ασφαλείας</string>\n    <string name=\"backup_import_failed\">Απέτυχε η ανάγνωση του αρχείου αντιγράφου ασφαλείας</string>\n    <string name=\"backup_export_success\">Η εξαγωγή αντιγράφου ασφαλείας ολοκληρώθηκε με επιτυχία</string>\n    <string name=\"backup_export_failed\">Δεν μπορώ να γράψω στο αρχείο αντιγράφου ασφαλείας</string>\n    <string name=\"sd_card\">Κάρτα SD</string>\n    <string name=\"sd_card_sync\">Συγχρονισμός κάρτας SD</string>\n    <string name=\"sd_card_summary\">Οι εργασίες συγχρονίζονται στην κάρτα SD. Οι αλλαγές συγχρονίζονται αμφίδρομα. Σημειώστε ότι διαγράφοντας τα αρχεία θα διαγραφούν οι εργασίες στην εφαρμογή!</string>\n    <string name=\"directory\">Κατάλογος</string>\n    <string name=\"cannot_write_to_directory\">Δεν είναι δυνατή η εγγραφή στον φάκελο</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notas</string>\n    <string name=\"title_create\">Nueva nota</string>\n    <string name=\"timemachine\">Máquina del tiempo</string>\n    <string name=\"time\">Hora</string>\n    <string name=\"done\">Hecho</string>\n    <string name=\"saved\">Guardado</string>\n    <string name=\"menu_save_and_add\">Guardar y añadir nota</string>\n    <string name=\"menu_delete\">Eliminar</string>\n    <string name=\"menu_deletelist\">Eliminar lista</string>\n    <string name=\"menu_sync\">Actualizar</string>\n    <string name=\"menu_share\">Compartir</string>\n    <string name=\"menu_preferences\">Ajustes</string>\n    <string name=\"menu_createlist\">Crear lista</string>\n    <string name=\"menu_clearcompleted\">Borrar completadas</string>\n    <string name=\"menu_setdefaultlist\">Establecer como predeterminada</string>\n    <string name=\"new_default_set\">Nuevo conjunto predeterminado</string>\n    <string name=\"menu_managelists\">Administrar listas</string>\n    <string name=\"add_item\">Añadir elemento</string>\n    <string name=\"delete_question\">¿Eliminar?</string>\n    <string name=\"delete_items_message\">¿Está seguro de que desea eliminar estos elementos\\?</string>\n    <string name=\"delete_item_message\">¿Eliminar este artículo\\?</string>\n    <string name=\"delete_list_message\">¿Eliminar esta lista\\?</string>\n    <string name=\"editor_due_date_hint\">Fecha</string>\n    <string name=\"editor_title_hint\">Título</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Editar nota</string>\n    <string name=\"default_style\">Estilo por defecto</string>\n    <string name=\"show_items_as_tasks\">Tareas que se pueden comprobar</string>\n    <string name=\"show_items_as_notes\">Notas sencillas</string>\n    <string name=\"sort_list_default\">Orden de clasificación predeterminado</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_due\">Fecha de vencimiento</string>\n    <string name=\"sort_list_updated\">Última actualización</string>\n    <string name=\"sort_list_manual\">Manual</string>\n    <string name=\"search_description\">Notas y tareas</string>\n    <string name=\"archive\">Archivo</string>\n    <string name=\"restore\">Restaurar</string>\n    <string name=\"restore_to\">Restaurar a</string>\n    <string name=\"search_hint\">Búsqueda</string>\n    <string name=\"settings_theme\">Tema actual</string>\n    <string name=\"settings_theme_dialog\">Elegir tema</string>\n    <string name=\"settings_lang\">Idioma</string>\n    <string name=\"settings_summary_theme_black\">Negro</string>\n    <string name=\"settings_summary_theme_dark\">Oscuro</string>\n    <string name=\"settings_summary_theme_light\">Claro</string>\n    <string name=\"settings_summary_theme_classic\">Clásico</string>\n    <string name=\"settings_cat_appearance\">Aspecto</string>\n    <string name=\"preference_preview_text\">Así es como se verá el texto en el editor</string>\n    <string name=\"settings_account_title\">Seleccione una cuenta</string>\n    <string name=\"settings_account_summary\">Pulse para elegir la cuenta</string>\n    <string name=\"settings_cat_syncing\">Sincronizando</string>\n    <string name=\"show_from_all_lists\">Todas las listas</string>\n    <string name=\"lists\">Listas</string>\n    <string name=\"deleted\">Borrado</string>\n    <string name=\"repeat\">Repetir</string>\n    <string name=\"once\">Una vez</string>\n    <string name=\"always\">Siempre</string>\n    <string name=\"permission_read_label\">leer notas y listas</string>\n    <string name=\"permission_read_desc\">Permite que la aplicación lea tus notas y listas relacionadas en No Nonsense Notes</string>\n    <string name=\"permission_write_label\">modificar notas y listas</string>\n    <string name=\"permission_write_desc\">Permite que la aplicación inserte, actualice y elimine las notas y las listas en No Nonsense Notes</string>\n    <string name=\"settings_list_dialog\">Seleccione una lista</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"import_data_question\">¿Importar datos?</string>\n    <string name=\"import_started\">Importando los datos…</string>\n    <string name=\"import_error\">Algo salió mal: %s</string>\n    <string name=\"move\">Mover</string>\n    <string name=\"move_to\">Mover a</string>\n    <string name=\"moved_x_to_list\">Movido %1$d a %2$s</string>\n    <string name=\"lock_note\">Bloquear nota</string>\n    <string name=\"unlock_note\">Desbloquear nota</string>\n    <string name=\"locked\">Bloqueada</string>\n    <string name=\"unlocked\">Desbloqueada</string>\n    <string name=\"password_required\">Contraseña requerida</string>\n    <string name=\"select_account\">Seleccione una cuenta</string>\n    <string name=\"password\">Contraseña</string>\n    <string name=\"password_set\">Contraseña establecida</string>\n    <string name=\"password_cleared\">Contraseña borrada</string>\n    <string name=\"passwords_dont_match\">Las contraseñas no concuerdan</string>\n    <string name=\"password_incorrect\">Contraseña incorrecta</string>\n    <string name=\"enter_password\">Introduzca la contraseña</string>\n    <string name=\"enter_new_password\">Introduzca una nueva contraseña</string>\n    <string name=\"confirm_new_password\">Confirme la nueva contraseña</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"clear_password\">Borrar contraseña</string>\n    <string name=\"password_info\">Cualquier nota puede bloquearse, limitando su visualización y modificación. Sigue siendo totalmente accesible en otros dispositivos.</string>\n    <string name=\"about\">Créditos</string>\n    <string name=\"sync_failed\">Error de sincronización</string>\n    <string name=\"sync_login_failed\">No se puede iniciar sesión para Google Tasks</string>\n    <string name=\"completed\">Completada</string>\n    <string name=\"date_header_overdue\">Pasadas</string>\n    <string name=\"date_header_today\">Hoy</string>\n    <string name=\"date_header_tomorrow\">Mañana</string>\n    <string name=\"date_header_future\">Más tarde</string>\n    <string name=\"date_header_none\">Sin fecha</string>\n    <string name=\"date_header_completed\">Completadas</string>\n    <string name=\"next_5_days\">Próximos 5 días</string>\n    <string name=\"next_n_days\">Próximos %d días</string>\n    <string name=\"this_week\">Esta semana</string>\n    <string name=\"please_select_note\">Por favor, seleccione o cree una nota nueva</string>\n    <string name=\"please_create_note\">Por favor, cree una nota nueva</string>\n    <string name=\"hide_checkbox\">Esconder casilla</string>\n    <string name=\"hide_checkbox_summary_on\">Casilla oculta</string>\n    <string name=\"hide_checkbox_summary_off\">Casilla mostrada</string>\n    <string name=\"hide_date\">Ocultar fecha de cumplimiento</string>\n    <string name=\"item_max_height\">Altura máxima de notas/tareas</string>\n    <string name=\"long_date_format\">Formato de fecha larga, para el panel</string>\n    <string name=\"short_date_format\">Formato de fecha corta, para la lista</string>\n    <string name=\"select_date\">Seleccionar fecha</string>\n    <string name=\"localedefault\">Localización por defecto</string>\n    <string name=\"hide_header\">Ocultar Encabezado de Widget</string>\n    <string name=\"transparency\">Transparencia</string>\n    <string name=\"notes_shortcut\">Acceso directa a Notes</string>\n    <string name=\"shortcut_help1\">Hace que el acceso directo abra una nueva nota en la lista seleccionada con un editor de texto abierto.</string>\n    <string name=\"loading_widget\">Cargando el widget…</string>\n    <string name=\"default_list\">Lista por defecto</string>\n    <string name=\"please_type_before_reminder\">Por favor, escribe un texto antes de añadir un recordatorio</string>\n    <string name=\"notecopied_one\">Una nota copiada</string>\n    <string name=\"notecopied_other\">Copiadas %d notas</string>\n    <string name=\"notedeleted_one\">Una nota borrada</string>\n    <string name=\"notedeleted_other\">Borradas %d notas</string>\n    <string name=\"selected_one\">Una nota seleccionada</string>\n    <string name=\"selected_other\">Seleccionadas %d notas</string>\n    <string name=\"background_sync\">Sincronizar en segundo plano</string>\n    <string name=\"background_sync_info\">Sincroniza una vez por hora</string>\n    <string name=\"sync_on_change\">Sincronizar cuando haya cambios</string>\n    <string name=\"sync_on_change_info\">Programa una sincronización poco depués de que una nota ha cambiado</string>\n    <string name=\"sync_on_start\">Sincroniza al inicio de la app</string>\n    <string name=\"sync_on_start_info\">Limita la sincronización a una vez cada 5 minutos</string>\n    <string name=\"snooze\">Silenciar</string>\n    <string name=\"silent\">Silencioso</string>\n    <string name=\"sound\">Sonido</string>\n    <string name=\"vibrate\">Vibrar</string>\n    <string name=\"reminders\">Recordatorio</string>\n    <string name=\"standard\">Estándar</string>\n    <string name=\"notification_prio_high\">Alta: encima, expandido</string>\n    <string name=\"notification_prio_low\">Baja: oculto, minimizado</string>\n    <string name=\"priority\">Prioridad</string>\n    <string name=\"notifications\">Notificaciones</string>\n    <string name=\"tasks\">Tareas</string>\n    <string name=\"all_tasks\">Todas las tareas</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restringir a las tareas en</string>\n    <string name=\"dashclock_show_overdue_tasks\">Mostrar tareas pasadas</string>\n    <string name=\"dashclock_overdue_tasks_show\">Se muestran las tareas pendientes</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Tareas pendientes ocultas</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Ajustes</string>\n    <string name=\"dashclock_pref_header_general\">General</string>\n    <string name=\"dashclock_today\">Hoy</string>\n    <string name=\"dashclock_tomorrow\">Mañana</string>\n    <string name=\"dashclock_next7\">Próximos 7 días</string>\n    <string name=\"dashclock_anytime\">Cualquier día</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Mostrar el máximo posible</string>\n    <string name=\"dashclock_show_only_the_next_task\">Mostrar solo la próxima tarea</string>\n    <string name=\"dashclock_header_shown\">Nombre de la lista en negrita</string>\n    <string name=\"dashclock_first_task_shown\">Título de la primera tarea en negrita</string>\n    <string name=\"dashclock_display_header\">Mostrar nombre de la lista</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Negrita</string>\n    <string name=\"italic\">Itálica</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"title_style\">Estilo del título</string>\n    <string name=\"title_font\">Fuente del título</string>\n    <string name=\"body_font\">Fuente del cuerpo</string>\n    <string name=\"clickable_links\">Enlaces cliqueables</string>\n    <string name=\"small\">Pequeño</string>\n    <string name=\"medium\">Medio</string>\n    <string name=\"large\">Largo</string>\n    <string name=\"text_size\">Tamaño del texto</string>\n    <string name=\"text\">Texto</string>\n    <string name=\"add_a_reminder\">Agregar un recordatorio</string>\n    <string name=\"backup\">Copia de seguridad</string>\n    <string name=\"backup_import\">Importar copia de seguridad</string>\n    <string name=\"backup_export_failed\">No se ha podido escribir en el archivo de la copia de seguridad</string>\n    <string name=\"sd_card\">Tarjeta SD</string>\n    <string name=\"sd_card_sync\">Sincronización de tarjetas SD</string>\n    <string name=\"sd_card_summary\">Las tareas siguen siendo las mismas en la aplicación y en la tarjeta SD. ¡Entonces, al eliminar los archivos, también se eliminan las tareas de la aplicación!</string>\n    <string name=\"directory\">Carpeta</string>\n    <string name=\"cannot_write_to_directory\">No se puede escribir en el directorio</string>\n    <string name=\"no_sync_method_chosen\">No eligiste un método de sincronización</string>\n    <string name=\"permission_denied\">No se puede continuar: denegaste el permiso</string>\n    <string name=\"notification_channel_name\">Recordatorios para las notas</string>\n    <string name=\"notification_channel_description\">Muestra el contenido de la nota en el momento del recordatorio elegido</string>\n    <string name=\"feature_is_WIP\">Esta característica es un W.I.P.</string>\n    <string name=\"file_picker_not_available\">El selector de archivos no está disponible</string>\n    <string name=\"delete_completed_tasks_question\">¿Eliminar todas las tareas completadas\\?</string>\n    <string name=\"imported_result\">Se importaron %1$d notas a %2$d listas</string>\n    <string name=\"navigation_drawer_close\">Cerrar el cajón de navegación</string>\n    <string name=\"drag_to_timetravel\">Arrastra para moverte en el tiempo</string>\n    <string name=\"navigation_drawer_open\">Abrir el cajón de navegación</string>\n    <string name=\"use_exact_alarms\">Utilizar alarmas exactas</string>\n    <string name=\"choose_backup_folder\">Elige una carpeta para la copia de seguridad</string>\n    <string name=\"for_older_devices\">Para dispositivos Android más antiguos</string>\n    <string name=\"app_about_donations\">Aceptamos donativos. Para apoyar nuestro trabajo, busca %1$s en %2$s</string>\n    <string name=\"disable_android_hibernation\">Desactivar la hibernación de Android</string>\n    <string name=\"msg_hibernation_already_off\">La hibernación de Android ya está APAGADA, no necesitas esto</string>\n    <string name=\"msg_enable_notifications\">Ve a ajustes -&gt; notificaciones para activar las notificaciones</string>\n    <string name=\"notifications_visibility\">Visibilidad de las notificaciones</string>\n    <string name=\"notifications_enabled\">Las notificaciones son visibles</string>\n    <string name=\"notifications_blocked\">Las notificaciones no son visibles, por lo que no verás los recordatorios. Toque aquí, busque la categoría de permisos y active las notificaciones para esta aplicación</string>\n    <string name=\"unavailable_chose_directory\">No disponible. Por favor, elija primero un directorio</string>\n    <string name=\"not_selected_yet\">Aún no seleccionado</string>\n    <string name=\"enable_sync\">Activar la sincronización</string>\n    <string name=\"enable_sync_desc\">Activa las funciones de sincronización y permite elegir una fuente de sincronización. Las copias de seguridad no se ven afectadas</string>\n    <string name=\"changelog_online\">Registro de cambios (en línea)</string>\n    <string name=\"canceled_note_locked\">Cancelada: la nota está bloqueada</string>\n    <string name=\"order_notes_by\">Ordenar notas por:</string>\n    <string name=\"select_all\">Seleccionar todo</string>\n    <string name=\"tutorial_online\">Tutorial (en línea)</string>\n    <string name=\"showcase_tutorial_title\">¿Te sientes perdido?</string>\n    <string name=\"showcase_tutorial_description\">Disponemos de un tutorial en línea. Encuéntrelo en la configuración</string>\n    <string name=\"welcome_note_title\">¡Bienvenid@s!</string>\n    <string name=\"welcome_note_row_2\">Abre esto para empezar.</string>\n    <string name=\"welcome_note_row_3\">Esta aplicación tiene un tutorial detallado. Puede encontrarlo en</string>\n    <string name=\"unsupported_readonly_file\">Archivo de solo lectura no soportado: %s</string>\n    <string name=\"show_completed_notes\">Mostrar notas finalizadas</string>\n    <string name=\"account\">Cuenta</string>\n    <string name=\"dashclock_due_upper_limit_title\">Límite a las tareas previstas</string>\n    <string name=\"backup_export\">Exportar copia de seguridad</string>\n    <string name=\"backup_import_msg\">¿Intentar importar la copia de seguridad de %1$s? Esto borrará la base de datos actual.</string>\n    <string name=\"backup_export_msg\">¿Exportar todas las notas a %1$s?</string>\n    <string name=\"backup_import_success\">Copia de seguridad importada</string>\n    <string name=\"backup_file_not_found\">No se ha podido encontrar el archivo de la copia de seguridad</string>\n    <string name=\"backup_export_success\">Copia de seguridad exportada</string>\n    <string name=\"directory_summary_msg\">Los archivos se guardan en %s\n\\na los que puedes acceder con tu aplicación de gestión de archivos. Al desinstalar la aplicación también se eliminarán estos archivos: para conservar tus notas, haz una copia de seguridad</string>\n    <string name=\"bigger_titles\">Títulos más grandes</string>\n    <string name=\"bigger_titles_summary\">El título es la primera línea</string>\n    <string name=\"sorting\">Clasificar</string>\n    <string name=\"undo\">Deshacer</string>\n    <string name=\"app_about_dev_team\">Creado por Jonas Kalderstam y mantenido por Campello Manuel</string>\n    <string name=\"show_elements_as\">Mostrar elementos como:</string>\n    <string name=\"version\">Versión: %s</string>\n    <string name=\"next_month\">Próximo mes</string>\n    <string name=\"next_year\">Próximo año</string>\n    <string name=\"prefs_improved_reliability\">Preferencias para mejorar la fiabilidad</string>\n    <string name=\"libraries_used\">Librerías</string>\n    <string name=\"allow_exact_reminders_summary\">Abre una página para permitir que esta aplicación envíe recordatorios de forma más fiable. Sólo se necesita desde Android 12 Snow Cone</string>\n    <string name=\"overwritten_in_newer_systems\">Puede anularse mediante determinados ajustes del canal de notificación</string>\n    <string name=\"notification_channel_settings\">Ajustes del canal de notificación</string>\n    <string name=\"open_channel_settings_description\">Abre una página para editar la configuración de las notificaciones de esta aplicación. Estos anulan cualquier configuración elegida en otro lugar</string>\n    <string name=\"exact_alarms_summary\">Utiliza más batería para mostrar los recordatorios de notificaciones de forma más fiable</string>\n    <string name=\"disable_battery_optimizations\">Desactivar las optimizaciones de la batería</string>\n    <string name=\"battery_optimizations_active\">Optimización activa de la batería</string>\n    <string name=\"battery_optimizations_inactive\">Optimización apagada de la batería</string>\n    <string name=\"allow_exact_reminders\">Permitir recordatorios precisos</string>\n    <string name=\"app_about_git_repo\">Esta aplicación es software libre con copyleft, disponible en https://github.com/spacecowboy/NotePad</string>\n    <string name=\"app_about_license\">Licencia GPLv3+, https://www.gnu.org/licenses</string>\n    <string name=\"app_about_bugreports\">Informe de errores y problemas en https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"app_about_translations\">Ayuda a traducir la aplicación en https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"disable_android_hibernation_desc\">Es una característica que hace que la aplicación sea menos fiable. Puede bloquear tus recordatorios. Ábrelo, busca algo como «suspender la actividad de la aplicación si no se utiliza» y desactívalo</string>\n    <string name=\"backup_import_failed\">No se ha podido leer el archivo de la copia de seguridad</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-et/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n<resources>\n    <string name=\"title_create\">Uus märge</string>\n    <string name=\"menu_delete\">Kustuta</string>\n    <string name=\"menu_deletelist\">Kustuta loend</string>\n    <string name=\"menu_sync\">Värskenda</string>\n    <string name=\"menu_share\">Jaga</string>\n    <string name=\"menu_preferences\">Seadistused</string>\n    <string name=\"menu_createlist\">Loo loend</string>\n    <string name=\"menu_clearcompleted\">Eemalda täidetud</string>\n    <string name=\"menu_setdefaultlist\">Märgi peamiseks loendiks</string>\n    <string name=\"menu_managelists\">Halda loendeid</string>\n    <string name=\"editor_due_date_hint\">Tähtaeg</string>\n    <string name=\"editor_title_hint\">Pealkiri</string>\n    <string name=\"editor_note_hint\">Märge</string>\n    <string name=\"resolve_edit\">Muuda märget</string>\n    <string name=\"search_hint\">Otsi</string>\n    <string name=\"settings_theme\">Kasutuselolev teema</string>\n    <string name=\"settings_theme_dialog\">Kasuta teemat</string>\n    <string name=\"settings_lang\">Keel</string>\n    <string name=\"settings_summary_theme_black\">Must</string>\n    <string name=\"settings_summary_theme_dark\">Tume</string>\n    <string name=\"settings_summary_theme_light\">Hele</string>\n    <string name=\"settings_cat_appearance\">Väljanägemine</string>\n    <string name=\"settings_account_title\">Vali konto</string>\n    <string name=\"settings_account_summary\">Kliki kontode vahetamiseks</string>\n    <string name=\"settings_cat_syncing\">Sünkroniseerimine</string>\n    <string name=\"show_from_all_lists\">Kõik loendid</string>\n    <string name=\"deleted\">Kustutatud</string>\n    <string name=\"settings_list_dialog\">Vali loend</string>\n    <string name=\"settings_list\">Loend</string>\n    <!--\n        Use few if language need it. In English is the same.\n        <item quantity=\"few\">Copied 2 notes.</item>\n    -->\n    <string name=\"lock_note\">Lukusta märge</string>\n    <string name=\"unlock_note\">Lukusta märge lahti</string>\n    <string name=\"locked\">Lukustatud</string>\n    <string name=\"unlocked\">Lukustamata</string>\n    <string name=\"password_required\">Nõutav on parool</string>\n    <string name=\"select_account\">Vali konto</string>\n    <string name=\"password\">Parool</string>\n    <string name=\"password_set\">Parool salvestatud</string>\n    <string name=\"password_cleared\">Parool kustutatud</string>\n    <string name=\"passwords_dont_match\">Paroolid ei kattu</string>\n    <string name=\"password_incorrect\">Parool ei ole õige</string>\n    <string name=\"enter_password\">Sisesta parool</string>\n    <string name=\"enter_new_password\">Sisesta uus parool</string>\n    <string name=\"confirm_new_password\">Kinnita uut parooli</string>\n    <string name=\"apply\">Rakenda</string>\n    <string name=\"clear_password\">Kustuta parool</string>\n    <string name=\"sync_failed\">Sünkroniseerimine ebaõnnestus</string>\n    <string name=\"sync_login_failed\">Google Tasks\\'i sisselogimine ebaõnnestus</string>\n    <string name=\"completed\">Täidetud</string>\n    <string name=\"date_header_overdue\">Üle aja</string>\n    <string name=\"date_header_today\">Täna</string>\n    <string name=\"date_header_tomorrow\">Homme</string>\n    <string name=\"date_header_future\">Hiljem</string>\n    <string name=\"date_header_none\">Ilma tähtajata</string>\n    <string name=\"date_header_completed\">Täidetud</string>\n    <string name=\"please_select_note\">Palun tee valik või loo uus märgend</string>\n    <string name=\"please_create_note\">Palun loo uus märgend</string>\n    <string name=\"hide_date\">Peida tähtajad</string>\n    <!-- settings string keys -->\n    <string name=\"notecopied_one\">Märge kopeeritud</string>\n    <string name=\"notecopied_other\">%d märget kopeeritud</string>\n    <string name=\"notedeleted_one\">Märge kustutatud</string>\n    <string name=\"notedeleted_other\">%d märget kustutatud</string>\n    <string name=\"selected_one\">Märge valitud</string>\n    <string name=\"selected_other\">%d märget valitud</string>\n    <string name=\"background_sync\">Taustal sünkroniseerimine</string>\n    <string name=\"background_sync_info\">Sünkroniseeri korra tunnis</string>\n    <string name=\"sync_on_change\">Sünkroniseeri muudatuste korral</string>\n    <string name=\"sync_on_change_info\">Määratle sünkroniseerimine vahetult peale märke muutmist </string>\n    <string name=\"sync_on_start\">Sünkroniseeri rakenduse käivitumisel</string>\n    <string name=\"sync_on_start_info\">Rakendub ainult esmakäivitusel</string>\n    <string name=\"silent\">Vaikne</string>\n    <string name=\"sound\">Heli</string>\n    <string name=\"vibrate\">Värin</string>\n    <string name=\"reminders\">Meeldetuletused</string>\n    <string name=\"standard\">Standartne</string>\n    <string name=\"notification_prio_high\">Kõrge: Tipus, tekst on laiendatud</string>\n    <string name=\"notification_prio_low\">Madal: Peidetud, tekst on minimeeritud</string>\n    <string name=\"priority\">Tähtsus</string>\n    <string name=\"notifications\">Teavitused</string>\n    <!-- \"localtime\" is replaced at runtime with 12/24 hour clock -->\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fa/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">یادداشت ها</string>\n    <string name=\"title_create\">یادداشت جدید</string>\n    <string name=\"timemachine\">ماشین زمان</string>\n    <string name=\"time\">زمان</string>\n    <string name=\"done\">حله</string>\n    <string name=\"saved\">ذخیره است خیالت راحت</string>\n    <string name=\"menu_save_and_add\">ذخیره یادداشت بعدهم اضافه یادداشت جدید</string>\n    <string name=\"menu_delete\">پاک کن</string>\n    <string name=\"menu_deletelist\">لیست رو پاک کن</string>\n    <string name=\"menu_sync\">همگام سازی</string>\n    <string name=\"menu_share\">فرستادن</string>\n    <string name=\"menu_preferences\">تنظیمات</string>\n    <string name=\"menu_createlist\">لیست جدید درست کن</string>\n    <string name=\"menu_clearcompleted\">تمیز کاری تمام شد</string>\n    <string name=\"menu_setdefaultlist\">این لیست باشه لیست پیش فرض</string>\n    <string name=\"menu_managelists\">ویرایش لیست</string>\n    <string name=\"add_item\">اضافه کردن مورد</string>\n    <string name=\"delete_question\">پاک کنم؟</string>\n    <string name=\"delete_items_message\">مطمئنی؟ این موردها رو پاک کنم؟</string>\n    <string name=\"delete_item_message\">مطمئنی؟ این مورد رو پاک کنم؟</string>\n    <string name=\"delete_list_message\">مطمئنی؟ این لیست رو پاک کنم؟</string>\n    <string name=\"editor_due_date_hint\">زمان انجام دادن</string>\n    <string name=\"editor_title_hint\">عنوان</string>\n    <string name=\"editor_note_hint\">یادداشت</string>\n    <string name=\"resolve_edit\">ویرایش یادداشت</string>\n    <string name=\"default_style\">حالت پیش فرض</string>\n    <string name=\"show_items_as_tasks\">مورد ها رو به عنوان کار نشان بده</string>\n    <string name=\"show_items_as_notes\">مورد ها رو به عنوان یادداشت نشان بده</string>\n    <string name=\"sort_list_default\">ترتیب پیش فرض</string>\n    <string name=\"sort_list_alphabetical\">به ترتیب حروف الفبا</string>\n    <string name=\"sort_list_due\">به ترتیب زمان انجام دادن</string>\n    <string name=\"sort_list_updated\">به ترتیب آخرین تغییر</string>\n    <string name=\"sort_list_manual\">ترتیب دستی</string>\n    <string name=\"search_description\">یادداشت ها و کارها</string>\n    <string name=\"archive\">آرشیو</string>\n    <string name=\"restore\">برگردان</string>\n    <string name=\"restore_to\">برگردان به</string>\n    <string name=\"search_hint\">جستجو</string>\n    <string name=\"settings_theme\">تم فعلی</string>\n    <string name=\"settings_theme_dialog\">از این تم استفاده کن</string>\n    <string name=\"settings_lang\">زبان</string>\n    <string name=\"settings_summary_theme_black\">سیاه</string>\n    <string name=\"settings_summary_theme_dark\">تیره</string>\n    <string name=\"settings_summary_theme_light\">روشن</string>\n    <string name=\"settings_summary_theme_classic\">کلاسیک</string>\n    <string name=\"settings_cat_appearance\">ظاهر</string>\n    <string name=\"preference_preview_text\">متن ویرایشگر اینجوری دیده می شه</string>\n    <string name=\"settings_account_title\">حساب کاربری رو انتخاب کن</string>\n    <string name=\"settings_account_summary\">تغییر حساب کاربری</string>\n    <string name=\"settings_cat_syncing\">هماهنگ کردن</string>\n    <string name=\"show_from_all_lists\">همه لیست ها</string>\n    <string name=\"deleted\">پاک شده ها</string>\n    <string name=\"repeat\">تکرار کن</string>\n    <string name=\"once\">یک بار</string>\n    <string name=\"always\">همیشه</string>\n    <string name=\"permission_read_label\">یادداشت ها و لیست ها را بخوان</string>\n    <string name=\"permission_read_desc\">به برنامه اجازه می دهد تا یادداشت های شما و لیست های مربوط به آن را بخواند</string>\n    <string name=\"permission_write_label\">یادداشت ها و لیست ها را تغییر بده</string>\n    <string name=\"permission_write_desc\">به برنامه اجازه می دهد تا یادداشت و لیست اضافه کند، یا آنها را بروز کرده یا پاک کند</string>\n    <string name=\"settings_list_dialog\">یک لیست انتخاب کن</string>\n    <string name=\"settings_list\">لیست</string>\n    <string name=\"drag_to_timetravel\">برای سفر در زمان این دسته رو جابجا کن</string>\n    <string name=\"import_data_question\">داده ها رو وارد کنم؟</string>\n    <string name=\"import_started\">دارم داده ها رو وارد می کنم…</string>\n    <!--\n        Use few if language need it. In English is the same.\n        <item quantity=\"few\">Copied 2 notes.</item>\n    -->\n    <!-- settings string keys -->\n    <!-- \"localtime\" is replaced at runtime with 12/24 hour clock -->\n    <string name=\"tasks\">کارها</string>\n    <string name=\"dashclock_pref_header_general\">عمومی</string>\n    <string name=\"dashclock_today\">امروز</string>\n    <string name=\"dashclock_tomorrow\">فردا</string>\n    <string name=\"dashclock_next7\">هفت روز دیگه</string>\n    <string name=\"dashclock_anytime\">هر وقتی</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">هر چقدر که شد نشان می ده</string>\n    <string name=\"dashclock_show_only_the_next_task\">فقط کار بعدی رو نشان بده</string>\n    <string name=\"dashclock_header_shown\">اسم لیست به صورت بولد نشان داده می شه</string>\n    <string name=\"dashclock_first_task_shown\">عنوان کار اول به صورت بولد نشان داده می شه</string>\n    <string name=\"dashclock_display_header\">اسم لیست رو نشان بده </string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-fi-rFI/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Muistiinpanot</string>\n    <string name=\"title_create\">Uusi muistiinpano</string>\n    <string name=\"timemachine\">Aikakone</string>\n    <string name=\"time\">Aika</string>\n    <string name=\"done\">Valmis</string>\n    <string name=\"saved\">Tallennettu</string>\n    <string name=\"menu_save_and_add\">Tallenna ja avaa uusi muistiinpano</string>\n    <string name=\"menu_delete\">Poista</string>\n    <string name=\"menu_deletelist\">Poista lista</string>\n    <string name=\"menu_sync\">Synkronoi</string>\n    <string name=\"menu_share\">Jaa</string>\n    <string name=\"menu_preferences\">Asetukset</string>\n    <string name=\"menu_createlist\">Luo uusi lista</string>\n    <string name=\"menu_clearcompleted\">Tyhjennä valmiit</string>\n    <string name=\"menu_setdefaultlist\">Aseta oletuslistaksi</string>\n    <string name=\"new_default_set\">Uusi oletus asetettu</string>\n    <string name=\"menu_managelists\">Hallitse listoja</string>\n    <string name=\"add_item\">Lisää kohde</string>\n    <string name=\"delete_question\">Poista?</string>\n    <string name=\"delete_items_message\">Haluatko varmasti poistaa nämä kohteet?</string>\n    <string name=\"delete_item_message\">Haluatko varmasti poistaa tämän kohteen?</string>\n    <string name=\"delete_list_message\">Haluatko varmasti poistaa listan?</string>\n    <string name=\"editor_due_date_hint\">Eräpäivä</string>\n    <string name=\"editor_title_hint\">Otsikko</string>\n    <string name=\"editor_note_hint\">Muistiinpano</string>\n    <string name=\"resolve_edit\">Muokkaa muistiinpanoa</string>\n    <string name=\"default_style\">Oletustyyli</string>\n    <string name=\"show_items_as_tasks\">Näytä kohteet tehtävinä</string>\n    <string name=\"show_items_as_notes\">Näytä kohteet muistiinpanoina</string>\n    <string name=\"sort_list_default\">Oletusjärjestys</string>\n    <string name=\"sort_list_alphabetical\">Järjestä aakkosittain</string>\n    <string name=\"sort_list_due\">Järjestä eräpäivän mukaan</string>\n    <string name=\"sort_list_updated\">Järjestä viimeksi päivitetyn mukaan</string>\n    <string name=\"sort_list_manual\">Järjestä itse</string>\n    <string name=\"search_description\">Muistiinpanot ja tehtävät</string>\n    <string name=\"archive\">Arkistoi</string>\n    <string name=\"restore\">Palauta</string>\n    <string name=\"restore_to\">Palauta sijaintiin</string>\n    <string name=\"search_hint\">Hae</string>\n    <string name=\"settings_theme\">Nykyinen teema</string>\n    <string name=\"settings_theme_dialog\">Käytä teemaa</string>\n    <string name=\"settings_lang\">Kieli</string>\n    <string name=\"settings_summary_theme_black\">Musta</string>\n    <string name=\"settings_summary_theme_dark\">Tumma</string>\n    <string name=\"settings_summary_theme_light\">Vaalea</string>\n    <string name=\"settings_summary_theme_classic\">Perinteinen</string>\n    <string name=\"settings_cat_appearance\">Ulkonäkö</string>\n    <string name=\"preference_preview_text\">Tältä editorin teksti näyttää</string>\n    <string name=\"settings_account_title\">Valitse käyttäjätili</string>\n    <string name=\"settings_account_summary\">Napauta vaihtaaksesi tiliä</string>\n    <string name=\"settings_cat_syncing\">Synkronointi</string>\n    <string name=\"show_from_all_lists\">Kaikki listat</string>\n    <string name=\"lists\">Listat</string>\n    <string name=\"deleted\">Poistettu</string>\n    <string name=\"repeat\">Toista</string>\n    <string name=\"once\">Kerran</string>\n    <string name=\"always\">Aina</string>\n    <string name=\"permission_read_label\">Lue muistiinpanoja ja listoja</string>\n    <string name=\"permission_read_desc\">Sallii sovelluksen lukea muistiinpanojasi ja niihin liittyviä listoja</string>\n    <string name=\"permission_write_label\">Muokkaa muistiinpanoja ja listoja</string>\n    <string name=\"permission_write_desc\">Sallii sovelluksen lisätä, päivittää ja poistaa muistiinpanoja ja listoja</string>\n    <string name=\"settings_list_dialog\">Valitse lista</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"drag_to_timetravel\">Raahaa aikamatkustaaksesi</string>\n    <string name=\"import_data_question\">Tuo tietoja?</string>\n    <string name=\"import_started\">Tietoja tuodaan…</string>\n    <string name=\"imported_result\">Tuotiin %1$d muistiinpanoa %2$d listasta</string>\n    <string name=\"import_error\">Jokin meni vikaan: %s</string>\n    <string name=\"move\">Siirrä</string>\n    <string name=\"move_to\">Siirrä sijaintiin</string>\n    <string name=\"moved_x_to_list\">Siirrettiin %1$d sijaintiin %2$s</string>\n    <string name=\"lock_note\">Lukitse muistiinpano</string>\n    <string name=\"unlock_note\">Avaa muistiinpanon lukitus</string>\n    <string name=\"locked\">Lukittu</string>\n    <string name=\"unlocked\">Lukitsematon</string>\n    <string name=\"password_required\">Salasana vaaditaan</string>\n    <string name=\"select_account\">Valitse käyttäjätili</string>\n    <string name=\"password\">Salasana</string>\n    <string name=\"password_set\">Salasana asetettu</string>\n    <string name=\"password_cleared\">Salasana poistettu</string>\n    <string name=\"passwords_dont_match\">Salasanat eivät täsmää</string>\n    <string name=\"password_incorrect\">Väärä salasana</string>\n    <string name=\"enter_password\">Syötä salasana</string>\n    <string name=\"enter_new_password\">Syötä uusi salasana</string>\n    <string name=\"confirm_new_password\">Vahvista uusi salasana</string>\n    <string name=\"apply\">Hyväksy</string>\n    <string name=\"clear_password\">Poista salasana</string>\n    <string name=\"password_info\">Muistiinpanojen lukitseminen rajoittaa pääsyn niiden katselemiseen ja muokkaamiseen. Muistiinpanot lukitaan yksitellen. Muistiinpanot ovat suojattu vain täällä ja muilla laitteilla on täysi pääsy niihin.</string>\n    <string name=\"about\">Tietoja</string>\n    <string name=\"sync_failed\">Synkronointi epäonnistui</string>\n    <string name=\"sync_login_failed\">Sisäänkirjautuminen epäonnistui, ei voitu yhdistää Google Tasks:iin</string>\n    <string name=\"completed\">Valmis</string>\n    <string name=\"date_header_overdue\">Myöhässä</string>\n    <string name=\"date_header_today\">Tänään</string>\n    <string name=\"date_header_tomorrow\">Huomenna</string>\n    <string name=\"date_header_future\">Myöhemmin</string>\n    <string name=\"date_header_none\">Ei päivämäärää</string>\n    <string name=\"date_header_completed\">Valmis</string>\n    <string name=\"next_5_days\">Seuraavat 5 päivää</string>\n    <string name=\"next_n_days\">Seuraavat %d päivää</string>\n    <string name=\"this_week\">Tämä viikko</string>\n    <string name=\"please_select_note\">Valitse tai luo muistiinpano</string>\n    <string name=\"please_create_note\">Luo muistiinpano</string>\n    <string name=\"hide_checkbox\">Piilota valintaruutu</string>\n    <string name=\"hide_checkbox_summary_on\">Valintaruutu piilotetaan</string>\n    <string name=\"hide_checkbox_summary_off\">Valintaruutu näytetään</string>\n    <string name=\"hide_date\">Piilota eräpäivä</string>\n    <string name=\"item_max_height\">Maksimikorkeus</string>\n    <string name=\"long_date_format\">Pitkä päivämäärä</string>\n    <string name=\"short_date_format\">Lyhyt päivämäärä</string>\n    <string name=\"select_date\">Valitse päivämäärä</string>\n    <string name=\"localedefault\">Oletus</string>\n    <string name=\"hide_header\">Piilota koko lisäohjelmatunniste</string>\n    <string name=\"transparency\">Läpinäkyvyys</string>\n    <string name=\"notes_shortcut\">Muistiinpanojen pikakuvake</string>\n    <string name=\"shortcut_help1\">Jos valittu, pikakuvake luo uuden muistiinpanon valittuun listaan ja avaa editorin. Muuten pikakuvake avaa valitun listan.</string>\n    <string name=\"loading_widget\">Ladataan lisäohjelmaa…</string>\n    <string name=\"default_list\">Oletuslista</string>\n    <string name=\"please_type_before_reminder\">Kirjoita jotakin ennen muistutuksen asettamista</string>\n    <string name=\"notecopied_one\">Muistiinpano kopioitu</string>\n    <string name=\"notecopied_other\">%d muistiinpanoa kopioitu</string>\n    <string name=\"notedeleted_one\">Muistiinpano poistettu</string>\n    <string name=\"notedeleted_other\">%d muistiinpanoa poistettu</string>\n    <string name=\"selected_one\">Muistiinpano valittu</string>\n    <string name=\"selected_other\">%d muistiinpanoa valittu</string>\n    <string name=\"background_sync\">Synkronointi taustalla</string>\n    <string name=\"background_sync_info\">Synkronoi kerran tunnissa</string>\n    <string name=\"sync_on_change\">Synkronoi muutosten yhteydessä</string>\n    <string name=\"sync_on_change_info\">Synkronoi, kun muistiinpano on muuttunut</string>\n    <string name=\"sync_on_start\">Synkronoi sovelluksen käynnistyessä</string>\n    <string name=\"sync_on_start_info\">Synkronoi korkeintaan joka 5. minuutti</string>\n    <string name=\"snooze\">Torkku</string>\n    <string name=\"silent\">Äänetön</string>\n    <string name=\"sound\">Ääni</string>\n    <string name=\"vibrate\">Värinä</string>\n    <string name=\"reminders\">Muistutukset</string>\n    <string name=\"standard\">Normaali</string>\n    <string name=\"notification_prio_high\">Korkea: Ylimpänä, laajennettu</string>\n    <string name=\"notification_prio_low\">Matala: Piilotettu, pienennetty</string>\n    <string name=\"priority\">Prioriteetti</string>\n    <string name=\"notifications\">Ilmoitukset</string>\n    <string name=\"tasks\">Tehtävät</string>\n    <string name=\"all_tasks\">Kaikki tehtävät</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Rajaa tietyssä listassa oleviin tehtäviin</string>\n    <string name=\"dashclock_show_overdue_tasks\">Näytä erääntyneet tehtävät</string>\n    <string name=\"dashclock_overdue_tasks_show\">Erääntyneet tehtävät näytetään</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Erääntyneet tehtävät piilotetaan</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense -muistiinpanojen asetukset</string>\n    <string name=\"dashclock_pref_header_general\">Yleiset</string>\n    <string name=\"dashclock_due_upper_limit_title\">Rajaa seuraavaksi erääntyviin tehtäviin</string>\n    <string name=\"dashclock_today\">Tänään</string>\n    <string name=\"dashclock_tomorrow\">Huomenna</string>\n    <string name=\"dashclock_next7\">Seuraavat 7 päivää</string>\n    <string name=\"dashclock_anytime\">Milloin tahansa</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Näyttää niin paljon kuin mahdollista</string>\n    <string name=\"dashclock_show_only_the_next_task\">Näytä vain seuraava tehtävä</string>\n    <string name=\"dashclock_header_shown\">Listan nimi lihavoidaan</string>\n    <string name=\"dashclock_first_task_shown\">Ensimmäisen tehtävän otsikko lihavoidaan</string>\n    <string name=\"dashclock_display_header\">Näytön tunniste</string>\n    <string name=\"editor\">Editori</string>\n    <string name=\"bold\">Lihavoitu</string>\n    <string name=\"italic\">Kursivoitu</string>\n    <string name=\"normal\">Normaali</string>\n    <string name=\"title_style\">Otsikon tyyli</string>\n    <string name=\"title_font\">Otsikon fontti</string>\n    <string name=\"body_font\">Kappaleen fontti</string>\n    <string name=\"clickable_links\">Klikattavat linkit</string>\n    <string name=\"small\">Pieni</string>\n    <string name=\"medium\">Keskikokoinen</string>\n    <string name=\"large\">Suuri</string>\n    <string name=\"text_size\">Tekstin koko</string>\n    <string name=\"text\">Teksti</string>\n    <string name=\"add_a_reminder\">Lisää muistutus</string>\n    <string name=\"backup\">Varmuuskopiointi</string>\n    <string name=\"backup_import\">Tuo varmuuskopio</string>\n    <string name=\"backup_export\">Vie varmuuskopio</string>\n    <string name=\"backup_import_msg\">Yritä tuoda varmuuskopio %1$s:sta? Tämä tyhjentää tämänhetkisen tietokannan.</string>\n    <string name=\"backup_export_msg\">Vie kaikki muistiinpanot %1$s:n?</string>\n    <string name=\"backup_import_success\">Varmuuskopio tuotu onnistuneesti</string>\n    <string name=\"backup_file_not_found\">Varmuuskopiotiedostoa ei löytynyt</string>\n    <string name=\"backup_import_failed\">Varmuuskopiotiedoston lukeminen epäonnistui</string>\n    <string name=\"backup_export_success\">Varmuuskopio viety onnistuneesti</string>\n    <string name=\"backup_export_failed\">Varmuuskopiotiedostoon ei voitu kirjoittaa</string>\n    <string name=\"sd_card\">Muistikortti</string>\n    <string name=\"sd_card_sync\">Muistikorttisynkronointi</string>\n    <string name=\"sd_card_summary\">Tehtävät peilataan muistikortille. Muutokset synkronoidaan molempiin suuntiin. Huomaa, että tiedostojen poistaminen poistaa tehtävät myös sovelluksesta!</string>\n    <string name=\"directory\">Hakemisto</string>\n    <string name=\"cannot_write_to_directory\">Hakemistoon ei voitu kirjoittaa</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"title_create\">Nouvelle note</string>\n    <string name=\"time\">Heure</string>\n    <string name=\"done\">Fait</string>\n    <string name=\"saved\">Sauvegardé</string>\n    <string name=\"menu_save_and_add\">Sauvegarder et créer une nouvelle note</string>\n    <string name=\"menu_delete\">Supprimer</string>\n    <string name=\"menu_deletelist\">Supprimer la liste</string>\n    <string name=\"menu_sync\">Actualiser</string>\n    <string name=\"menu_share\">Partager</string>\n    <string name=\"menu_preferences\">Paramètres</string>\n    <string name=\"menu_createlist\">Créer une liste</string>\n    <string name=\"menu_clearcompleted\">Effacer les notes complétées</string>\n    <string name=\"menu_setdefaultlist\">Définir comme liste par défaut</string>\n    <string name=\"new_default_set\">Liste par défaut définie</string>\n    <string name=\"menu_managelists\">Gérer les listes</string>\n    <string name=\"add_item\">Ajouter un élément</string>\n    <string name=\"delete_question\">Supprimer ?</string>\n    <string name=\"delete_items_message\">Supprimer ces éléments ?</string>\n    <string name=\"delete_item_message\">Supprimer cet élément ?</string>\n    <string name=\"delete_list_message\">Supprimer cette liste ?</string>\n    <string name=\"editor_due_date_hint\">Échéance</string>\n    <string name=\"editor_title_hint\">Titre</string>\n    <string name=\"resolve_edit\">Éditer la note</string>\n    <string name=\"default_style\">Style par défaut</string>\n    <string name=\"show_items_as_tasks\">Afficher les éléments comme tâches</string>\n    <string name=\"show_items_as_notes\">Afficher les éléments comme notes</string>\n    <string name=\"sort_list_default\">Ordre de tri par défault</string>\n    <string name=\"sort_list_alphabetical\">Ordonner alphabétiquement</string>\n    <string name=\"sort_list_due\">Ordonner par date d\\'échéance</string>\n    <string name=\"sort_list_updated\">Ordonner par date de modification</string>\n    <string name=\"sort_list_manual\">Ordonner manuellement</string>\n    <string name=\"search_description\">Notes et tâches</string>\n    <string name=\"restore\">Restaurer</string>\n    <string name=\"restore_to\">Restaurer à</string>\n    <string name=\"search_hint\">Rechercher</string>\n    <string name=\"settings_theme\">Thème actuel</string>\n    <string name=\"settings_theme_dialog\">Choisir un thème</string>\n    <string name=\"settings_lang\">Langue</string>\n    <string name=\"settings_summary_theme_black\">Noir</string>\n    <string name=\"settings_summary_theme_dark\">Sombre</string>\n    <string name=\"settings_summary_theme_light\">Clair</string>\n    <string name=\"settings_summary_theme_classic\">Classique</string>\n    <string name=\"settings_cat_appearance\">Apparence</string>\n    <string name=\"preference_preview_text\">Voici comment le texte apparaîtra dans l\\'éditeur</string>\n    <string name=\"settings_account_title\">Choisir un compte</string>\n    <string name=\"settings_account_summary\">Cliquer pour changer de compte</string>\n    <string name=\"settings_cat_syncing\">Synchronisation</string>\n    <string name=\"show_from_all_lists\">Toutes les listes</string>\n    <string name=\"lists\">Listes</string>\n    <string name=\"deleted\">Supprimé</string>\n    <string name=\"repeat\">Répéter</string>\n    <string name=\"once\">Une fois</string>\n    <string name=\"always\">Toujours</string>\n    <string name=\"permission_read_label\">lire les notes et les listes</string>\n    <string name=\"permission_read_desc\">Autorise l\\'application à lire vos notes et listes</string>\n    <string name=\"permission_write_label\">modifier les notes et les listes</string>\n    <string name=\"permission_write_desc\">Autorise l\\'application à créer, modifier et supprimer des notes et listes</string>\n    <string name=\"settings_list_dialog\">Choisir une liste</string>\n    <string name=\"settings_list\">Liste</string>\n    <string name=\"drag_to_timetravel\">Glisser pour revenir en arrière</string>\n    <string name=\"import_data_question\">Importer les données ?</string>\n    <string name=\"import_started\">Importation des données…</string>\n    <string name=\"imported_result\">%1$d notes importées dans %2$d listes</string>\n    <string name=\"import_error\">Quelque chose n\\'a pas fonctionné : %s</string>\n    <string name=\"move\">Déplacer</string>\n    <string name=\"move_to\">Déplacer vers</string>\n    <string name=\"moved_x_to_list\">%1$d déplacé vers %2$s</string>\n    <string name=\"lock_note\">Verrouiller la note</string>\n    <string name=\"unlock_note\">Déverrouiller la note</string>\n    <string name=\"locked\">Verrouillé</string>\n    <string name=\"unlocked\">Déverrouillé</string>\n    <string name=\"password_required\">Mot de passe requis</string>\n    <string name=\"select_account\">Choisir un compte</string>\n    <string name=\"password\">Mot de passe</string>\n    <string name=\"password_set\">Mot de passe défini</string>\n    <string name=\"password_cleared\">Mot de passe effacé</string>\n    <string name=\"passwords_dont_match\">Les mots de passe ne correspondent pas</string>\n    <string name=\"password_incorrect\">Mot de passe incorrect</string>\n    <string name=\"enter_password\">Entrer le mot de passe</string>\n    <string name=\"enter_new_password\">Entrer le nouveau mot de passe</string>\n    <string name=\"confirm_new_password\">Confirmer le nouveau mot de passe</string>\n    <string name=\"apply\">Appliquer</string>\n    <string name=\"clear_password\">Effacer le mot de passe</string>\n    <string name=\"password_info\">Après avoir défini un mot de passe, vous devrez le saisir pour voir les notes verrouillées. Le mot de passe protégera la note elle-même, mais pas le titre ni la date. La note ne sera pas cryptée donc pourra toujours être lue via Google Mail/Agenda. Si vous oubliez votre mot de passe, synchronisez les notes avec Google et réinstallez l\\'application.</string>\n    <string name=\"about\">À propos</string>\n    <string name=\"sync_failed\">La synchronisation a échouée</string>\n    <string name=\"sync_login_failed\">La connexion à Google Tâches a échouée</string>\n    <string name=\"completed\">Complété</string>\n    <string name=\"date_header_overdue\">En retard</string>\n    <string name=\"date_header_today\">Aujourd\\'hui</string>\n    <string name=\"date_header_tomorrow\">Demain</string>\n    <string name=\"date_header_future\">Plus tard</string>\n    <string name=\"date_header_none\">Pas de date</string>\n    <string name=\"date_header_completed\">Complété</string>\n    <string name=\"next_5_days\">5 prochains jours</string>\n    <string name=\"next_n_days\">%d prochains jours</string>\n    <string name=\"this_week\">Cette semaine</string>\n    <string name=\"please_select_note\">SVP sélectionner ou créer une note</string>\n    <string name=\"please_create_note\">SVP créer une note</string>\n    <string name=\"hide_checkbox\">Masquer la case à cocher</string>\n    <string name=\"hide_checkbox_summary_on\">La case sera masquée</string>\n    <string name=\"hide_checkbox_summary_off\">La case sera affichée</string>\n    <string name=\"hide_date\">Masquer la date d\\'échéance</string>\n    <string name=\"item_max_height\">Hauteur maximale des notes/tâches</string>\n    <string name=\"long_date_format\">Format de date longue</string>\n    <string name=\"short_date_format\">Format de date courte</string>\n    <string name=\"select_date\">Date sélectionnée</string>\n    <string name=\"localedefault\">Paramètres régionaux par défaut</string>\n    <string name=\"hide_header\">Masquer l\\'en-tête du widget</string>\n    <string name=\"transparency\">Opacité</string>\n    <string name=\"notes_shortcut\">Raccourci</string>\n    <string name=\"shortcut_help1\">Si coché, le raccourci créera une nouvelle note dans la liste sélectionnée et ouvrira l\\'éditeur. Si non coché, le raccourci ouvrira la liste sélectionnée.</string>\n    <string name=\"loading_widget\">Chargement du widget…</string>\n    <string name=\"default_list\">Liste par défaut</string>\n    <string name=\"please_type_before_reminder\">SVP tapez du texte avant de régler un rappel</string>\n    <string name=\"notecopied_one\">Une note copiée</string>\n    <string name=\"notecopied_other\">%d notes copiées</string>\n    <string name=\"notedeleted_one\">Une note supprimée</string>\n    <string name=\"notedeleted_other\">%d notes supprimées</string>\n    <string name=\"selected_one\">Une note sélectionnée</string>\n    <string name=\"selected_other\">%d notes sélectionnées</string>\n    <string name=\"background_sync\">Synchronisation en arrière-plan</string>\n    <string name=\"background_sync_info\">Synchroniser une fois l\\'heure</string>\n    <string name=\"sync_on_change\">Synchronisation des changements</string>\n    <string name=\"sync_on_change_info\">Planifie une synchronisation peu de temps après la mise à jour d\\'une note</string>\n    <string name=\"sync_on_start\">Synchronisation au démarrage</string>\n    <string name=\"sync_on_start_info\">Se synchronisera toutes les 5 minutes</string>\n    <string name=\"snooze\">Répéter</string>\n    <string name=\"silent\">Silence</string>\n    <string name=\"sound\">Sonnerie</string>\n    <string name=\"vibrate\">Vibreur</string>\n    <string name=\"reminders\">Rappels</string>\n    <string name=\"standard\">Normal</string>\n    <string name=\"notification_prio_high\">Haute : Au-dessus, élargie</string>\n    <string name=\"notification_prio_low\">Basse : Masquée, minimisée</string>\n    <string name=\"priority\">Priorité</string>\n    <string name=\"tasks\">Tâches</string>\n    <string name=\"all_tasks\">Toutes les tâches</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restreindre les tâches à une liste spécifique</string>\n    <string name=\"dashclock_show_overdue_tasks\">Afficher les tâches passées dues</string>\n    <string name=\"dashclock_overdue_tasks_show\">Les tâches seront affichées</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Les tâches passées dues seront masquées</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Paramètres de NoNonesense Notes</string>\n    <string name=\"dashclock_pref_header_general\">Général</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limiter aux tâches dues prochainement</string>\n    <string name=\"dashclock_today\">Aujourd\\'hui</string>\n    <string name=\"dashclock_tomorrow\">Demain</string>\n    <string name=\"dashclock_next7\">7 prochains jours</string>\n    <string name=\"dashclock_anytime\">N\\'importe quand</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">En affichera autant que possible</string>\n    <string name=\"dashclock_show_only_the_next_task\">Afficher seulement la prochaine tâche</string>\n    <string name=\"dashclock_header_shown\">Le nom de la liste s\\'affichera en gras</string>\n    <string name=\"dashclock_first_task_shown\">Le titre de la première tâches s\\'affichera en premier</string>\n    <string name=\"dashclock_display_header\">Afficher l\\'en-tête</string>\n    <string name=\"editor\">Éditeur</string>\n    <string name=\"bold\">Gras</string>\n    <string name=\"italic\">Italique</string>\n    <string name=\"title_style\">Style du titre</string>\n    <string name=\"title_font\">Police du titre</string>\n    <string name=\"body_font\">Police du corps de texte</string>\n    <string name=\"clickable_links\">Liens cliquables</string>\n    <string name=\"small\">Petit</string>\n    <string name=\"medium\">Moyen</string>\n    <string name=\"large\">Grand</string>\n    <string name=\"text_size\">Taille du texte</string>\n    <string name=\"text\">Texte</string>\n    <string name=\"add_a_reminder\">Ajouter un rappel</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-gl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notas</string>\n    <string name=\"title_create\">Nova nota</string>\n    <string name=\"timemachine\">Máquina do tempo</string>\n    <string name=\"time\">Hora</string>\n    <string name=\"done\">Feito</string>\n    <string name=\"saved\">Gardada</string>\n    <string name=\"menu_save_and_add\">Gardar e engadir nova nota</string>\n    <string name=\"menu_delete\">Eliminar</string>\n    <string name=\"menu_deletelist\">Eliminar listaxe</string>\n    <string name=\"menu_sync\">Sincronizar</string>\n    <string name=\"menu_share\">Compartir</string>\n    <string name=\"menu_preferences\">Axustes</string>\n    <string name=\"menu_createlist\">Crear nova listaxe</string>\n    <string name=\"menu_clearcompleted\">Eliminar as completadas</string>\n    <string name=\"menu_setdefaultlist\">Definir como listaxe predefinida</string>\n    <string name=\"new_default_set\">Novo valor predefinido</string>\n    <string name=\"menu_managelists\">Editar listaxe</string>\n    <string name=\"add_item\">Engadir elemento</string>\n    <string name=\"delete_question\">Eliminar?</string>\n    <string name=\"delete_items_message\">Tes a certeza de querer eliminar estes elementos?</string>\n    <string name=\"delete_item_message\">Tes a certeza de querer eliminar este elemento?</string>\n    <string name=\"delete_list_message\">Tes a certeza de querer eliminar esta listaxe?</string>\n    <string name=\"editor_due_date_hint\">Data límite</string>\n    <string name=\"editor_title_hint\">Título</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Editar nota</string>\n    <string name=\"default_style\">Estilo predefinido</string>\n    <string name=\"show_items_as_tasks\">Mostrar elementos como tarefas</string>\n    <string name=\"show_items_as_notes\">Mostrar elementos como notas</string>\n    <string name=\"sort_list_default\">Orde predefinida</string>\n    <string name=\"sort_list_alphabetical\">Orde alfabética</string>\n    <string name=\"sort_list_due\">Orde por data límite</string>\n    <string name=\"sort_list_updated\">Orde pola última actualización</string>\n    <string name=\"sort_list_manual\">Orde manual</string>\n    <string name=\"search_description\">Notas e tarefas</string>\n    <string name=\"archive\">Arquivo</string>\n    <string name=\"restore\">Restaurar</string>\n    <string name=\"restore_to\">Restaurar a</string>\n    <string name=\"search_hint\">Procurar</string>\n    <string name=\"settings_theme\">Tema actual</string>\n    <string name=\"settings_theme_dialog\">Utilizar tema</string>\n    <string name=\"settings_lang\">Idioma</string>\n    <string name=\"settings_summary_theme_black\">Escuro</string>\n    <string name=\"settings_summary_theme_dark\">Escuro</string>\n    <string name=\"settings_summary_theme_light\">Claro</string>\n    <string name=\"settings_summary_theme_classic\">Clásico</string>\n    <string name=\"settings_cat_appearance\">Aparencia</string>\n    <string name=\"preference_preview_text\">Así é como se verá o texto no editor</string>\n    <string name=\"settings_account_title\">Seleccionar unha conta</string>\n    <string name=\"settings_account_summary\">Toca para elixir a conta</string>\n    <string name=\"settings_cat_syncing\">Sincronización</string>\n    <string name=\"show_from_all_lists\">Todas as listaxes</string>\n    <string name=\"lists\">Listaxes</string>\n    <string name=\"deleted\">Eliminada</string>\n    <string name=\"repeat\">Repetir</string>\n    <string name=\"once\">Unha vez</string>\n    <string name=\"always\">Sempre</string>\n    <string name=\"permission_read_label\">ler notas e listaxes</string>\n    <string name=\"permission_read_desc\">Permite ao aplicativo ler as túas notas e as listaxes relacionadas</string>\n    <string name=\"permission_write_label\">modificar notas e listaxes</string>\n    <string name=\"permission_write_desc\">Permite ao aplicativo inserir, actualizar e eliminar notas e listaxes</string>\n    <string name=\"settings_list_dialog\">Seleccionar unha listaxe</string>\n    <string name=\"settings_list\">Listaxe</string>\n    <string name=\"drag_to_timetravel\">Arrastra para viaxares no tempo</string>\n    <string name=\"import_data_question\">Importar datos?</string>\n    <string name=\"import_started\">Importando datos…</string>\n    <string name=\"imported_result\">Importáronse %1$d notas de %2$d listaxes</string>\n    <string name=\"import_error\">Algo foi mal: %s</string>\n    <string name=\"move\">Mover</string>\n    <string name=\"move_to\">Mover a</string>\n    <string name=\"moved_x_to_list\">Moveuse %1$d a %2$s</string>\n    <string name=\"lock_note\">Bloquear nota</string>\n    <string name=\"unlock_note\">Desbloquear nota</string>\n    <string name=\"locked\">Bloqueada</string>\n    <string name=\"unlocked\">Desbloqueada</string>\n    <string name=\"password_required\">Contrasinal obrigatorio</string>\n    <string name=\"select_account\">Seleccionar unha conta</string>\n    <string name=\"password\">Contrasinal</string>\n    <string name=\"password_set\">Definir contrasinal</string>\n    <string name=\"password_cleared\">Contrasinal eliminado</string>\n    <string name=\"passwords_dont_match\">Os contrasinais non coinciden</string>\n    <string name=\"password_incorrect\">Contrasinal incorrecto</string>\n    <string name=\"enter_password\">Inserir contrasinal</string>\n    <string name=\"enter_new_password\">Inserir o novo contrasinal</string>\n    <string name=\"confirm_new_password\">Confirmar o novo contrasinal</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"clear_password\">Borrar contrasinal</string>\n    <string name=\"password_info\">Ao establecer un contrasinal limitarase o acceso para ver e modificar as notas. Bloquéanse de xeito individual e só estarán protexidas aquí, mais seguirán completamente accesibles noutros dispositivos.</string>\n    <string name=\"about\">Acerca de</string>\n    <string name=\"sync_failed\">Erro na sincronización</string>\n    <string name=\"sync_login_failed\">Erro ao iniciar sesión; non se puido conectar con Google Tasks</string>\n    <string name=\"completed\">Completada</string>\n    <string name=\"date_header_overdue\">Pasadas</string>\n    <string name=\"date_header_today\">Hoxe</string>\n    <string name=\"date_header_tomorrow\">Mañá</string>\n    <string name=\"date_header_future\">Máis tarde</string>\n    <string name=\"date_header_none\">Sen data</string>\n    <string name=\"date_header_completed\">Completada</string>\n    <string name=\"next_5_days\">Vindeiros 5 días</string>\n    <string name=\"next_n_days\">Vindeiros %d días</string>\n    <string name=\"this_week\">Esta semana</string>\n    <string name=\"please_select_note\">Por favor, selecciona ou crea unha nova nota</string>\n    <string name=\"please_create_note\">Por favor, crea unha nova nota</string>\n    <string name=\"hide_checkbox\">Agochar casa de verificación</string>\n    <string name=\"hide_checkbox_summary_on\">As caixas de verificación agocharanse</string>\n    <string name=\"hide_checkbox_summary_off\">As caixas de verificación mostraranse</string>\n    <string name=\"hide_date\">Agochar a data límite</string>\n    <string name=\"item_max_height\">Altura máxima das notas/tarefas</string>\n    <string name=\"long_date_format\">Formato de data longo</string>\n    <string name=\"short_date_format\">Formato de data curto</string>\n    <string name=\"select_date\">Seleccionar data</string>\n    <string name=\"localedefault\">Localización predefinida</string>\n    <string name=\"hide_header\">Agochar cabeceira do widget</string>\n    <string name=\"transparency\">Transparencia</string>\n    <string name=\"notes_shortcut\">Atallo ás notas</string>\n    <string name=\"shortcut_help1\">De se marcar, o atallo creará unha nova nota na listaxe seleccionada e abrirá o editor. De non o estar, unicamente abrirá a listaxe seleccionada.</string>\n    <string name=\"loading_widget\">Cargando o widget…</string>\n    <string name=\"default_list\">Listaxe predefinida</string>\n    <string name=\"please_type_before_reminder\">Por favor, escribe algo antes de definir un recordatorio</string>\n    <string name=\"notecopied_one\">1 nota copiada</string>\n    <string name=\"notecopied_other\">%d notas copiadas</string>\n    <string name=\"notedeleted_one\">1 nota eliminada</string>\n    <string name=\"notedeleted_other\">%d notas eliminadas</string>\n    <string name=\"selected_one\">1 nota seleccionada</string>\n    <string name=\"selected_other\">%d notas seleccionadas</string>\n    <string name=\"background_sync\">Sincronizar en segundo plano</string>\n    <string name=\"background_sync_info\">Sincronización a cada hora</string>\n    <string name=\"sync_on_change\">Sincronizar cando haxa cambios</string>\n    <string name=\"sync_on_change_info\">Sincronización ao pouco de modificar unha nota</string>\n    <string name=\"sync_on_start\">Sincronizar ao iniciar o aplicativo</string>\n    <string name=\"sync_on_start_info\">Sincronización como máximo cada 5 minutos</string>\n    <string name=\"snooze\">Atrasar</string>\n    <string name=\"silent\">En silencio</string>\n    <string name=\"sound\">Son</string>\n    <string name=\"vibrate\">Vibrar</string>\n    <string name=\"reminders\">Recordatorios</string>\n    <string name=\"standard\">Estándar</string>\n    <string name=\"notification_prio_high\">Alta: parte superior, expandida</string>\n    <string name=\"notification_prio_low\">Baixa: oculta, minimizada</string>\n    <string name=\"priority\">Prioridade</string>\n    <string name=\"notifications\">Notificacións</string>\n    <string name=\"tasks\">Tarefas</string>\n    <string name=\"all_tasks\">Todas as tarefas</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restrición de tarefas</string>\n    <string name=\"dashclock_show_overdue_tasks\">Mostrar tarefas pasadas</string>\n    <string name=\"dashclock_overdue_tasks_show\">Mostraranse as tarefas pasadas</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Agocharanse as tarefas pasadas</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Axustes de NoNonsense Notes</string>\n    <string name=\"dashclock_pref_header_general\">Xeral</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limitar as tarefas pasadas ás últimas</string>\n    <string name=\"dashclock_today\">Hoxe</string>\n    <string name=\"dashclock_tomorrow\">Mañá</string>\n    <string name=\"dashclock_next7\">Vindeiros 7 días</string>\n    <string name=\"dashclock_anytime\">En calquera momento</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Mostrarase todo o que for posible</string>\n    <string name=\"dashclock_show_only_the_next_task\">Mostrar só a próxima tarefa</string>\n    <string name=\"dashclock_header_shown\">O nome da listaxe aparecerá en letra grosa</string>\n    <string name=\"dashclock_first_task_shown\">O título da primeira tarefa aparecerá en letra grosa</string>\n    <string name=\"dashclock_display_header\">Mostrar o nome da listaxe</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Grosa</string>\n    <string name=\"italic\">Cursiva</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"title_style\">Estilo do título</string>\n    <string name=\"title_font\">Fonte do título</string>\n    <string name=\"body_font\">Fonte</string>\n    <string name=\"clickable_links\">Ligazóns activadas</string>\n    <string name=\"small\">Pequeno</string>\n    <string name=\"medium\">Mediano</string>\n    <string name=\"large\">Grande</string>\n    <string name=\"text_size\">Tamaño do texto</string>\n    <string name=\"text\">Texto</string>\n    <string name=\"add_a_reminder\">Engadir un recordatorio</string>\n    <string name=\"backup\">Copia de seguranza</string>\n    <string name=\"backup_import\">Importar copia de seguranza</string>\n    <string name=\"backup_export\">Exportar copia de seguranza</string>\n    <string name=\"backup_import_msg\">Tentar importar a copia de seguranza de %1$s? Isto eliminará a actual base de datos.</string>\n    <string name=\"backup_export_msg\">Exportar todas as notas a %1$s?</string>\n    <string name=\"backup_import_success\">Copia de seguranza importada satisfactoriamente</string>\n    <string name=\"backup_file_not_found\">Non se atopou o ficheiro da copia de seguranza</string>\n    <string name=\"backup_import_failed\">Erro ao ler o ficheiro da copia de seguranza</string>\n    <string name=\"backup_export_success\">Copia de seguranza exportada satisfactoriamente</string>\n    <string name=\"backup_export_failed\">Non se pode escribir no ficheiro da copia de seguranza</string>\n    <string name=\"sd_card\">Tarxeta SD</string>\n    <string name=\"sd_card_sync\">Sincronizar tarxeta SD</string>\n    <string name=\"sd_card_summary\">As tarefas espéllanse na tarxeta SD. Os cambios sincronízanse en ambos os sentidos. Repara en que a eliminación dos ficheiros supón a eliminación das tarefas do aplicativo!</string>\n    <string name=\"directory\">Directorio</string>\n    <string name=\"cannot_write_to_directory\">Non se pode escribir no directorio</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-hu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Jegyzetek</string>\n    <string name=\"title_create\">Új jegyzet</string>\n    <string name=\"timemachine\">Időgép</string>\n    <string name=\"time\">Idő</string>\n    <string name=\"done\">Kész</string>\n    <string name=\"saved\">Mentve</string>\n    <string name=\"menu_save_and_add\">Mentés és új létrehozása</string>\n    <string name=\"menu_delete\">Törlés</string>\n    <string name=\"menu_deletelist\">Lista törlése</string>\n    <string name=\"menu_sync\">Frissítés</string>\n    <string name=\"menu_share\">Megosztás</string>\n    <string name=\"menu_preferences\">Beállítások</string>\n    <string name=\"menu_createlist\">Lista létrehozása</string>\n    <string name=\"menu_clearcompleted\">Kész jegyzetek törlése</string>\n    <string name=\"menu_setdefaultlist\">Alapértelmezett lista</string>\n    <string name=\"new_default_set\">Új alapértelmezett lista beállítva</string>\n    <string name=\"menu_managelists\">Lista kezelése</string>\n    <string name=\"add_item\">Elem hozzáadása</string>\n    <string name=\"delete_question\">Törlés?</string>\n    <string name=\"delete_items_message\">Biztosan törli a kijelölt elemeket?</string>\n    <string name=\"delete_item_message\">Biztosan törli ezt az elemet?</string>\n    <string name=\"delete_list_message\">Biztosan törli ezt a listát?</string>\n    <string name=\"editor_due_date_hint\">Határidő</string>\n    <string name=\"editor_title_hint\">Cím</string>\n    <string name=\"editor_note_hint\">Szöveg</string>\n    <string name=\"resolve_edit\">Szerkesztés</string>\n    <string name=\"default_style\">Alapértelemezett stílus</string>\n    <string name=\"show_items_as_tasks\">Feladat stílus</string>\n    <string name=\"show_items_as_notes\">Jegyzet stílus</string>\n    <string name=\"sort_list_default\">Alapértelmezett rendezés</string>\n    <string name=\"sort_list_alphabetical\">Ábécé sorrend</string>\n    <string name=\"sort_list_due\">Lejárati dátum szerint</string>\n    <string name=\"sort_list_updated\">Utolsó frissítés szerint</string>\n    <string name=\"sort_list_manual\">Kézi rendezés</string>\n    <string name=\"search_description\">Jegyzetek és feladatok</string>\n    <string name=\"archive\">Archívum</string>\n    <string name=\"restore\">Visszaállítás</string>\n    <string name=\"restore_to\">Visszaállítás helye</string>\n    <string name=\"search_hint\">Keresés</string>\n    <string name=\"settings_theme\">Jelenlegi téma</string>\n    <string name=\"settings_theme_dialog\">Téma használata</string>\n    <string name=\"settings_lang\">Program nyelve</string>\n    <string name=\"settings_summary_theme_black\">Fekete</string>\n    <string name=\"settings_summary_theme_dark\">Sötét</string>\n    <string name=\"settings_summary_theme_light\">Világos</string>\n    <string name=\"settings_summary_theme_classic\">Klasszikus</string>\n    <string name=\"settings_cat_appearance\">Megjelenés</string>\n    <string name=\"preference_preview_text\">Így fog megjelenni a szöveg a szerkesztőben</string>\n    <string name=\"settings_account_title\">Válassz fiókot</string>\n    <string name=\"settings_account_summary\">Kattints a fiók váltásához</string>\n    <string name=\"settings_cat_syncing\">Szinkronizálás</string>\n    <string name=\"show_from_all_lists\">Összes lista</string>\n    <string name=\"lists\">Listák</string>\n    <string name=\"deleted\">Törölve</string>\n    <string name=\"repeat\">Ismétlés</string>\n    <string name=\"once\">Egyszer</string>\n    <string name=\"always\">Mindig</string>\n    <string name=\"permission_read_label\">listák és jegyzetek olvasása</string>\n    <string name=\"permission_read_desc\">Engedély a jegyzetek és kapcsolódó listák olvasására</string>\n    <string name=\"permission_write_label\">listák és jegyzetek módosítása</string>\n    <string name=\"permission_write_desc\">Engedély a jegyzetek és listák módosítására és törlésére</string>\n    <string name=\"settings_list_dialog\">Válassz egy listát</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"drag_to_timetravel\">Húzd az időutazáshoz</string>\n    <string name=\"import_data_question\">Szeretné importálni az adatokat?</string>\n    <string name=\"import_started\">Adatok importálása folyamatban…</string>\n    <string name=\"imported_result\">%1$d jegyzet importálása %2$d listába</string>\n    <string name=\"import_error\">Hiba: %s</string>\n    <string name=\"move\">Áthelyezés</string>\n    <string name=\"move_to\">Áthelyezés</string>\n    <string name=\"moved_x_to_list\">%1$d elem áthelyezve a következő listába: %2$s</string>\n    <string name=\"lock_note\">Zárolás</string>\n    <string name=\"unlock_note\">Feloldás</string>\n    <string name=\"locked\">Zárolva</string>\n    <string name=\"unlocked\">Feloldva</string>\n    <string name=\"password_required\">Jelszó szükséges</string>\n    <string name=\"select_account\">Válassz egy fiókot</string>\n    <string name=\"password\">Jelszó</string>\n    <string name=\"password_set\">Jelszó megadása</string>\n    <string name=\"password_cleared\">Jelszó törölve</string>\n    <string name=\"passwords_dont_match\">Jelszó nem egyezik</string>\n    <string name=\"password_incorrect\">Hibás jelszó</string>\n    <string name=\"enter_password\">Add meg a jelszavadat</string>\n    <string name=\"enter_new_password\">Add meg az új jelszavadat</string>\n    <string name=\"confirm_new_password\">Erősítsd meg az új jelszavadat</string>\n    <string name=\"apply\">Alkalmaz</string>\n    <string name=\"clear_password\">Jelszó törlése</string>\n    <string name=\"password_info\">A jelszó megadása szükséges a zárolt jegyzetek megjelenítéséhez. Ez csak a szövegmezőt védi, így a cím és a határidő még mindig elérhető. A jelszó nem titkosítja a jegyzetet, amely így látható a Gmailben és a Naptárban. Ha elfelejted a jelszavadat, szinkronizáld a Google-fiókodat és telepítsd újra az alkalmazást.</string>\n    <string name=\"about\">Névjegy</string>\n    <string name=\"sync_failed\">Szinkronizálási hiba</string>\n    <string name=\"sync_login_failed\">Bejelentkezési hiba, nem lehetett csatlakozni a Google Feladatokhoz</string>\n    <string name=\"completed\">Kész</string>\n    <string name=\"date_header_overdue\">Lejárt</string>\n    <string name=\"date_header_today\">Ma</string>\n    <string name=\"date_header_tomorrow\">Holnap</string>\n    <string name=\"date_header_future\">Később</string>\n    <string name=\"date_header_none\">Nincs dátum</string>\n    <string name=\"date_header_completed\">Kész</string>\n    <string name=\"next_5_days\">A következő 5 nap</string>\n    <string name=\"next_n_days\">Elkövetkező %d nap</string>\n    <string name=\"this_week\">E hét</string>\n    <string name=\"please_select_note\">Válassz ki vagy hozz létre egy listát</string>\n    <string name=\"please_create_note\">Hozz létre egy jegyzetet</string>\n    <string name=\"hide_checkbox\">Jelölőnégyzet elrejtése</string>\n    <string name=\"hide_checkbox_summary_on\">Jelölőnégyzet el van rejtve</string>\n    <string name=\"hide_checkbox_summary_off\">Jelölőnégyzet látható</string>\n    <string name=\"hide_date\">Határidő elrejtése</string>\n    <string name=\"item_max_height\">Jegyzet/Feladat magassága</string>\n    <string name=\"long_date_format\">Hosszú dátum formátum</string>\n    <string name=\"short_date_format\">Rövid dátum formátum</string>\n    <string name=\"select_date\">Dátum</string>\n    <string name=\"localedefault\">Alapértelmezett</string>\n    <string name=\"hide_header\">Widget fejrész elrejtése</string>\n    <string name=\"transparency\">Átlátszóság</string>\n    <string name=\"notes_shortcut\">Jegyzetek parancsikon</string>\n    <string name=\"shortcut_help1\">Ezt az opciót bekapcsolva, a parancsikon (megnyitva a szerkesztőt) létrehoz egy új jegyzetet a kiválasztott listában. Kikapcsolva a kiválasztott listát nyitja meg.</string>\n    <string name=\"loading_widget\">Widget betölt…</string>\n    <string name=\"default_list\">Alapértelmezett lista</string>\n    <string name=\"please_type_before_reminder\">Emlékeztető beállítása elött kérem írjon valamit</string>\n    <string name=\"notecopied_one\">1 jegyzet másolva</string>\n    <string name=\"notecopied_other\">%d jegyzet másolva</string>\n    <string name=\"notedeleted_one\">1 jegyzet törölve</string>\n    <string name=\"notedeleted_other\">%d jegyzet törölve</string>\n    <string name=\"selected_one\">1 jegyzet kijelölve</string>\n    <string name=\"selected_other\">%d jegyzet kijelölve</string>\n    <string name=\"background_sync\">Szinkronizálás a háttérben</string>\n    <string name=\"background_sync_info\">Óránként egyszer szinkronizál</string>\n    <string name=\"sync_on_change\">Szinkronizálás változáskor</string>\n    <string name=\"sync_on_change_info\">Jegyzet változása után nem sokkal szinkronizál</string>\n    <string name=\"sync_on_start\">Szinkronizálás alkalmazás indításakor</string>\n    <string name=\"sync_on_start_info\">Nagyjából 5 percenként szinkronizál</string>\n    <string name=\"snooze\">Szundi</string>\n    <string name=\"silent\">Csendes</string>\n    <string name=\"sound\">Hang</string>\n    <string name=\"vibrate\">Rezgés</string>\n    <string name=\"reminders\">Emlékeztetők</string>\n    <string name=\"standard\">Alapértelmezett</string>\n    <string name=\"notification_prio_high\">Magas: Legfelül, kiterjesztve</string>\n    <string name=\"notification_prio_low\">Alacsony: Rejtve, minimalizálva</string>\n    <string name=\"priority\">Prioritás</string>\n    <string name=\"notifications\">Értesítések</string>\n    <string name=\"tasks\">Feladatok</string>\n    <string name=\"all_tasks\">Minden feladat</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Megjelenő jegyzetek korlátozása lista szerint</string>\n    <string name=\"dashclock_show_overdue_tasks\">Lejárt feladatok megjelenítése</string>\n    <string name=\"dashclock_overdue_tasks_show\">A lejárt feladatok meg fognak jelenni</string>\n    <string name=\"dashclock_overdue_tasks_hide\">A lejárt feladatok el lesznek rejtve</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Jegyzetek beállításai</string>\n    <string name=\"dashclock_pref_header_general\">Általános</string>\n    <string name=\"dashclock_due_upper_limit_title\">Legkésőbbi feladatok ideje</string>\n    <string name=\"dashclock_today\">Ma</string>\n    <string name=\"dashclock_tomorrow\">Holnap</string>\n    <string name=\"dashclock_next7\">Egy hét múlva</string>\n    <string name=\"dashclock_anytime\">Bármikor</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Annyit jelenít meg amennyit csak lehet</string>\n    <string name=\"dashclock_show_only_the_next_task\">Csak a soron következő feladat megjelenítése</string>\n    <string name=\"dashclock_header_shown\">A lista neve félkövéren jelenik meg</string>\n    <string name=\"dashclock_first_task_shown\">Az első feladat címe lesz félkövér</string>\n    <string name=\"dashclock_display_header\">Fejléc megjelenítése</string>\n    <string name=\"editor\">Szerkesztő</string>\n    <string name=\"bold\">Félkövér</string>\n    <string name=\"italic\">Dőlt</string>\n    <string name=\"normal\">Normál</string>\n    <string name=\"title_style\">Cím stílusa</string>\n    <string name=\"title_font\">Cím betűtípusa</string>\n    <string name=\"body_font\">Szöveg betűtípusa</string>\n    <string name=\"clickable_links\">Kattinható linkek</string>\n    <string name=\"small\">Kicsi</string>\n    <string name=\"medium\">Közepes</string>\n    <string name=\"large\">Nagy</string>\n    <string name=\"text_size\">Szöveg betűméret</string>\n    <string name=\"text\">Szöveg</string>\n    <string name=\"add_a_reminder\">Emlékeztető hozzáadása</string>\n    <string name=\"backup\">Biztonsági mentés</string>\n    <string name=\"backup_import\">Importálás</string>\n    <string name=\"backup_export\">Exportálás</string>\n    <string name=\"backup_import_msg\">Szeretné importálni a biztonsági mentést a %1$s fájlból? Ez felülírja a jelenlegi adatbázist.</string>\n    <string name=\"backup_export_msg\">Szeretné exportálni az adatbázist a %1$s fájlba?</string>\n    <string name=\"backup_import_success\">Sikeres importálás</string>\n    <string name=\"backup_file_not_found\">A fájl nem található</string>\n    <string name=\"backup_import_failed\">A fájl nem olvasható</string>\n    <string name=\"backup_export_success\">Sikeres exportálás</string>\n    <string name=\"backup_export_failed\">Fájl nem írható</string>\n    <string name=\"sd_card\">SD kártya</string>\n    <string name=\"sd_card_sync\">SD kártya szinkronizálás</string>\n    <string name=\"sd_card_summary\">A bejegyzések az SD kártyára vannak tükrözve. A változások mind két irányban szinronizálva vannak. A fájlok törlésével a bejegyzések is törlődnek az alkalmazásból!</string>\n    <string name=\"directory\">Mappa</string>\n    <string name=\"cannot_write_to_directory\">Mappa nem írható</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-in-rID/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Catatan</string>\n    <string name=\"title_create\">Catatan Baru</string>\n    <string name=\"timemachine\">Mesin waktu</string>\n    <string name=\"time\">Waktu</string>\n    <string name=\"done\">Selesai</string>\n    <string name=\"saved\">Disimpan</string>\n    <string name=\"menu_save_and_add\">Simpan dan tambahkan catatan</string>\n    <string name=\"menu_delete\">Hapus</string>\n    <string name=\"menu_deletelist\">Hapus daftar</string>\n    <string name=\"menu_sync\">Muat Ulang</string>\n    <string name=\"menu_share\">Bagikan</string>\n    <string name=\"menu_preferences\">Pengaturan</string>\n    <string name=\"menu_createlist\">Buat Daftar</string>\n    <string name=\"menu_clearcompleted\">Hapus yang sudah selesai</string>\n    <string name=\"menu_setdefaultlist\">Tetapkan sebagai daftar default</string>\n    <string name=\"new_default_set\">Set standar baru</string>\n    <string name=\"menu_managelists\">Edit daftar</string>\n    <string name=\"add_item\">Tambah item</string>\n    <string name=\"delete_question\">Hapus?</string>\n    <string name=\"delete_items_message\">Hapus item ini\\?</string>\n    <string name=\"delete_item_message\">Hapus item ini\\?</string>\n    <string name=\"delete_list_message\">Hapus daftar ini\\?</string>\n    <string name=\"editor_due_date_hint\">Tenggat waktu</string>\n    <string name=\"editor_title_hint\">Judul</string>\n    <string name=\"editor_note_hint\">Catatan</string>\n    <string name=\"resolve_edit\">Ubah Catatan</string>\n    <string name=\"default_style\">Gaya default</string>\n    <string name=\"show_items_as_tasks\">tugas yang dapat dicentang</string>\n    <string name=\"show_items_as_notes\">catatan sederhana</string>\n    <string name=\"sort_list_default\">Urutan penyortiran default</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_due\">Jatuh tempo</string>\n    <string name=\"sort_list_updated\">Terakhir diperbarui</string>\n    <string name=\"sort_list_manual\">Manual</string>\n    <string name=\"search_description\">Catatan dan tugas</string>\n    <string name=\"archive\">Arsip</string>\n    <string name=\"restore\">Kembalikan</string>\n    <string name=\"restore_to\">Kembalikan ke</string>\n    <string name=\"search_hint\">Cari</string>\n    <string name=\"settings_theme\">Tema saat ini</string>\n    <string name=\"settings_theme_dialog\">Gunakan tema</string>\n    <string name=\"settings_lang\">Bahasa</string>\n    <string name=\"settings_summary_theme_black\">Hitam</string>\n    <string name=\"settings_summary_theme_dark\">Gelap</string>\n    <string name=\"settings_summary_theme_light\">Terang</string>\n    <string name=\"settings_summary_theme_classic\">Klasik</string>\n    <string name=\"settings_cat_appearance\">Tampilan</string>\n    <string name=\"preference_preview_text\">Ini adalah bagaimana teks editor akan muncul</string>\n    <string name=\"settings_account_title\">Pilih Akun</string>\n    <string name=\"settings_account_summary\">Tekan untuk memilih akun</string>\n    <string name=\"settings_cat_syncing\">Sinkronisasi</string>\n    <string name=\"show_from_all_lists\">Semua daftar</string>\n    <string name=\"lists\">Daftar</string>\n    <string name=\"deleted\">Terhapus</string>\n    <string name=\"repeat\">Ulangi</string>\n    <string name=\"once\">Sekali</string>\n    <string name=\"always\">Selalu</string>\n    <string name=\"permission_read_label\">Baca catatan dan daftar</string>\n    <string name=\"permission_read_desc\">Memungkinkan aplikasi membaca catatan Anda dan daftar terkait di No Nonsense Notes</string>\n    <string name=\"permission_write_label\">Ubah catatan dan daftar</string>\n    <string name=\"permission_write_desc\">Izinkan aplikasi untuk menyisipkan, memperbarui, dan menghapus catatan dan daftar di No Nonsense Notes</string>\n    <string name=\"settings_list_dialog\">Pilih Daftar</string>\n    <string name=\"settings_list\">Daftar</string>\n    <string name=\"drag_to_timetravel\">Seret ke waktu perjalanan</string>\n    <string name=\"import_data_question\">Impor data?</string>\n    <string name=\"import_started\">Mengimpor data…</string>\n    <string name=\"imported_result\">Mengimpor catatan %1$d ke dalam daftar %2$d</string>\n    <string name=\"import_error\">Ada sesuatu yang salah: %s</string>\n    <string name=\"move\">Pindah</string>\n    <string name=\"move_to\">Pindah ke</string>\n    <string name=\"moved_x_to_list\">Pindah %1$d ke %2$s</string>\n    <string name=\"lock_note\">Kunci catatan</string>\n    <string name=\"unlock_note\">Buka kunci catatan</string>\n    <string name=\"locked\">Terkunci</string>\n    <string name=\"unlocked\">Kunci Terbuka</string>\n    <string name=\"password_required\">Diperlukan sandi</string>\n    <string name=\"select_account\">Pilih akun</string>\n    <string name=\"password\">Sandi</string>\n    <string name=\"password_set\">Sandi terpasang</string>\n    <string name=\"password_cleared\">Sandi terhapus</string>\n    <string name=\"passwords_dont_match\">Sandi tidak cocok</string>\n    <string name=\"password_incorrect\">Sandi salah</string>\n    <string name=\"enter_password\">Masukkan sandi</string>\n    <string name=\"enter_new_password\">Masukkan sandi baru</string>\n    <string name=\"confirm_new_password\">Konfirmasi sandi baru</string>\n    <string name=\"apply\">Terapkan</string>\n    <string name=\"clear_password\">Hapus sandi</string>\n    <string name=\"password_info\">Setiap catatan dapat dikunci, dibatasi tampilan dan memodifikasinya. Catatan tersebut tetap dapat diakses sepenuhnya di perangkat lain.</string>\n    <string name=\"about\">Tentang</string>\n    <string name=\"sync_failed\">Snkronisasi gagal</string>\n    <string name=\"sync_login_failed\">Tidak dapat masuk untuk menggunakan Google Task</string>\n    <string name=\"completed\">Selesai</string>\n    <string name=\"date_header_overdue\">Terlambat</string>\n    <string name=\"date_header_today\">Hari ini</string>\n    <string name=\"date_header_tomorrow\">Besok</string>\n    <string name=\"date_header_future\">Nanti</string>\n    <string name=\"date_header_none\">Tanpa tanggal</string>\n    <string name=\"date_header_completed\">Selesai</string>\n    <string name=\"next_5_days\">5 hari berikutnya</string>\n    <string name=\"next_n_days\">Berikutnya %d hari</string>\n    <string name=\"this_week\">Minggu ini</string>\n    <string name=\"please_select_note\">Harap pilih atau buat catatan</string>\n    <string name=\"please_create_note\">Harap membuat catatan</string>\n    <string name=\"hide_checkbox\">Sembunyikan kotak centang</string>\n    <string name=\"hide_checkbox_summary_on\">Kotak centang disembunyikan</string>\n    <string name=\"hide_checkbox_summary_off\">Kotak centang ditampilkan</string>\n    <string name=\"hide_date\">Sembunyikan tanggal jatuh tempo</string>\n    <string name=\"item_max_height\">Tinggi maksimal catatan / tugas</string>\n    <string name=\"long_date_format\">Format tanggal panjang, untuk panel</string>\n    <string name=\"short_date_format\">Format tanggal pendek, untuk daftar</string>\n    <string name=\"select_date\">Pilih tanggal</string>\n    <string name=\"localedefault\">Lokal standar</string>\n    <!-- settings string keys -->\n    <string name=\"hide_header\">Sembunyikan seluruh header pada widget</string>\n    <string name=\"transparency\">Transparansi</string>\n    <string name=\"notes_shortcut\">Catatan pintas</string>\n    <string name=\"shortcut_help1\">Membuat pintasan membuka catatan baru dalam daftar yang dipilih dengan editor teks terbuka.</string>\n    <string name=\"loading_widget\">Memuat widget…</string>\n    <string name=\"default_list\">Daftar default</string>\n    <string name=\"please_type_before_reminder\">Harap ketik beberapa teks sebelum menambahkan pengingat</string>\n    <string name=\"notecopied_one\">Disalin 1 catatan</string>\n    <string name=\"notecopied_other\">Disalin %d catatan</string>\n    <string name=\"notedeleted_one\">Dihapus 1 catatan</string>\n    <string name=\"notedeleted_other\">Dihapus %d catatan</string>\n    <string name=\"selected_one\">Terpilih satu catatan</string>\n    <string name=\"selected_other\">Dipilih %d catatan</string>\n    <string name=\"background_sync\">Sinkronisasi di latar belakang</string>\n    <string name=\"background_sync_info\">Menyinkronisasi setiap jam</string>\n    <string name=\"sync_on_change\">Menyinkronisasi pada saat berubah</string>\n    <string name=\"sync_on_change_info\">Jadwalkan sinkronisasi sebentar setelah catatan berubah</string>\n    <string name=\"sync_on_start\">Menyinkronisasi pada saat aplikasi mulai</string>\n    <string name=\"sync_on_start_info\">Batasi sinkronisasi hingga setiap 5 menit sekali</string>\n    <string name=\"snooze\">Tidur sebentar</string>\n    <string name=\"silent\">Diam</string>\n    <string name=\"sound\">Suara</string>\n    <string name=\"vibrate\">Getar</string>\n    <string name=\"reminders\">pengingat</string>\n    <string name=\"standard\">Standar</string>\n    <string name=\"notification_prio_high\">Tinggi: Di atas, diperluas</string>\n    <string name=\"notification_prio_low\">Rendah: Tersembunyi, dipersempit</string>\n    <string name=\"priority\">Prioritas</string>\n    <string name=\"notifications\">notifikasi</string>\n    <string name=\"tasks\">Tugas</string>\n    <string name=\"all_tasks\">Semua tugas</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Batasi tugas di</string>\n    <string name=\"dashclock_show_overdue_tasks\">Tampilkan tugas yang sudah lewat</string>\n    <string name=\"dashclock_overdue_tasks_show\">Tugas yang sudah lewat waktu ditampilkan</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Tugas yang tertunda disembunyikan</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Pengaturan</string>\n    <string name=\"dashclock_pref_header_general\">Umum</string>\n    <string name=\"dashclock_due_upper_limit_title\">Batasi tugas sampai selambat-lambatnya</string>\n    <string name=\"dashclock_today\">Hari ini</string>\n    <string name=\"dashclock_tomorrow\">Besok</string>\n    <string name=\"dashclock_next7\">7 hari berikutnya</string>\n    <string name=\"dashclock_anytime\">Kapan saja</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Tampilkan sebanyak mungkin</string>\n    <string name=\"dashclock_show_only_the_next_task\">Hanya tampilkan tugas selanjutnya</string>\n    <string name=\"dashclock_header_shown\">Nama daftar yang dicetak tebal</string>\n    <string name=\"dashclock_first_task_shown\">Judul tugas pertama yang dicetak tebal</string>\n    <string name=\"dashclock_display_header\">Nama daftar tampilan</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Tebal</string>\n    <string name=\"italic\">Miring</string>\n    <string name=\"normal\">Biasa</string>\n    <string name=\"title_style\">Gaya Judul</string>\n    <string name=\"title_font\">Font untuk judul</string>\n    <string name=\"body_font\">Font tubuh</string>\n    <string name=\"clickable_links\">Tautan yang dapat diklik</string>\n    <string name=\"small\">Kecil</string>\n    <string name=\"medium\">Medium</string>\n    <string name=\"large\">Besar</string>\n    <string name=\"text_size\">Ukuran teks</string>\n    <string name=\"text\">Teks</string>\n    <string name=\"add_a_reminder\">Tambahkan pengingat</string>\n    <string name=\"backup\">Cadangan</string>\n    <string name=\"backup_import\">Impor backup</string>\n    <string name=\"backup_export\">Ekspor cadangan</string>\n    <string name=\"backup_import_msg\">Cobalah untuk mengimpor cadangan dari %1$s? Ini akan menghapus database saat ini.</string>\n    <string name=\"backup_export_msg\">Ekspor semua catatan ke %1$s?</string>\n    <string name=\"backup_import_success\">Cadangan diimpor</string>\n    <string name=\"backup_file_not_found\">Tidak dapat menemukan file cadangan</string>\n    <string name=\"backup_import_failed\">Tidak dapat membaca file cadangan</string>\n    <string name=\"backup_export_success\">Cadangan diekspor</string>\n    <string name=\"backup_export_failed\">Tidak dapat menulis ke file cadangan</string>\n    <string name=\"sd_card\">Kartu SD</string>\n    <string name=\"sd_card_sync\">Sinkronkan Kartu SD</string>\n    <string name=\"sd_card_summary\">Tugas tetap sama antara aplikasi dan kartu SD. Menghapus file berarti menghapus tugas dalam aplikasi!</string>\n    <string name=\"directory\">Folder</string>\n    <string name=\"cannot_write_to_directory\">Tidak bisa menulis ke direktori</string>\n    <string name=\"undo\">Batalkan</string>\n    <string name=\"delete_completed_tasks_question\">Hapus semua tugas yang sudah selesai\\?</string>\n    <string name=\"navigation_drawer_open\">Membuka laci navigasi</string>\n    <string name=\"navigation_drawer_close\">Tutup laci navigasi</string>\n    <string name=\"notification_channel_description\">Menampilkan konten catatan pada saat pengingat yang dipilih</string>\n    <string name=\"permission_denied\">Tidak dapat melanjutkan: Anda menolak izin</string>\n    <string name=\"sorting\">Penyortiran</string>\n    <string name=\"notification_channel_name\">Pengingat untuk catatan</string>\n    <string name=\"feature_is_WIP\">Fitur ini adalah W.I.P.</string>\n    <string name=\"file_picker_not_available\">Pemilih file tidak tersedia</string>\n    <string name=\"no_sync_method_chosen\">Anda tidak memilih metode sinkronisasi</string>\n    <string name=\"choose_backup_folder\">Pilih folder cadangan</string>\n    <string name=\"exact_alarms_summary\">Menggunakan lebih banyak baterai untuk menampilkan pengingat notifikasi dengan cara yang lebih andal</string>\n    <string name=\"use_exact_alarms\">Gunakan alarm yang tepat</string>\n    <string name=\"disable_battery_optimizations\">Matikan pengoptimalan baterai</string>\n    <string name=\"battery_optimizations_active\">Pengoptimalan baterai aktif</string>\n    <string name=\"battery_optimizations_inactive\">Pengoptimalan baterai nonaktif</string>\n    <string name=\"allow_exact_reminders\">Izinkan pengingat yang serupa</string>\n    <string name=\"show_completed_notes\">Tampilkan catatan yang sudah selesai</string>\n    <string name=\"account\">Akun</string>\n    <string name=\"directory_summary_msg\">File disimpan dalam %s \n\\ndapat Anda akses dengan aplikasi pengelola file Anda. Menghapus instalan aplikasi juga akan menghapus file-file ini: untuk menyimpan catatan Anda, buatlah cadangan</string>\n    <string name=\"bigger_titles_summary\">Judul adalah baris pertama</string>\n    <string name=\"bigger_titles\">Judul yang lebih besar</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-is/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "app/src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Note</string>\n    <string name=\"title_create\">Nuova nota</string>\n    <string name=\"timemachine\">Cronologia Nota</string>\n    <string name=\"time\">Tempo</string>\n    <string name=\"done\">Fatto</string>\n    <string name=\"saved\">Salvata</string>\n    <string name=\"menu_save_and_add\">Salva e apre una nuova nota</string>\n    <string name=\"menu_delete\">Elimina</string>\n    <string name=\"menu_deletelist\">Elimina elenco</string>\n    <string name=\"menu_sync\">Sincronizza</string>\n    <string name=\"menu_share\">Condividi</string>\n    <string name=\"menu_preferences\">Impostazioni</string>\n    <string name=\"menu_createlist\">Crea elenco</string>\n    <string name=\"menu_clearcompleted\">Rimuovi completate</string>\n    <string name=\"menu_setdefaultlist\">Imposta come elenco predefinito</string>\n    <string name=\"menu_managelists\">Gestisci elenchi</string>\n    <string name=\"add_item\">Aggiungi elemento</string>\n    <string name=\"delete_question\">Eliminare?</string>\n    <string name=\"delete_items_message\">Vuoi veramente eliminare questi elementi\\?</string>\n    <string name=\"delete_item_message\">Vuoi veramente eliminare questo elemento\\?</string>\n    <string name=\"delete_list_message\">Vuoi veramente eliminare l\\'elenco, archiviando le note ?</string>\n    <string name=\"editor_due_date_hint\">Data di scadenza</string>\n    <string name=\"editor_title_hint\">Titolo</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Modifica nota</string>\n    <string name=\"default_style\">Stile predefinito</string>\n    <string name=\"show_items_as_tasks\">attività completabili</string>\n    <string name=\"show_items_as_notes\">note semplici</string>\n    <string name=\"sort_list_default\">Ordinamento predefinito</string>\n    <string name=\"sort_list_alphabetical\">Alfabetico</string>\n    <string name=\"sort_list_due\">Per data</string>\n    <string name=\"sort_list_updated\">Per ultima modifica</string>\n    <string name=\"sort_list_manual\">Manuale</string>\n    <string name=\"search_description\">Note e attività</string>\n    <string name=\"archive\">Vedi archivio</string>\n    <string name=\"restore\">Ripristina</string>\n    <string name=\"restore_to\">Ripristina in</string>\n    <string name=\"search_hint\">Cerca</string>\n    <string name=\"settings_theme\">Tema attuale</string>\n    <string name=\"settings_theme_dialog\">Applica tema</string>\n    <string name=\"settings_lang\">Lingua</string>\n    <string name=\"settings_summary_theme_black\">Nero OLED</string>\n    <string name=\"settings_summary_theme_dark\">Scuro</string>\n    <string name=\"settings_summary_theme_light\">Chiaro</string>\n    <string name=\"settings_summary_theme_classic\">Classico</string>\n    <string name=\"settings_cat_appearance\">Aspetto</string>\n    <string name=\"preference_preview_text\">Ecco come apparirà il testo nell\\'editor</string>\n    <string name=\"settings_account_title\">Seleziona un account</string>\n    <string name=\"settings_account_summary\">Tocca per cambiare account</string>\n    <string name=\"settings_cat_syncing\">Sincronizzazione</string>\n    <string name=\"show_from_all_lists\">Tutti gli elenchi</string>\n    <string name=\"deleted\">Eliminata</string>\n    <string name=\"repeat\">Ripeti</string>\n    <string name=\"once\">Una sola vota</string>\n    <string name=\"always\">Sempre</string>\n    <string name=\"permission_read_label\">leggi note ed elenchi No Nonsense Notes</string>\n    <string name=\"permission_read_desc\">Permetti all\\'applicazione di leggere le tue note e i relativi elenchi di No Nonsense Notes</string>\n    <string name=\"permission_write_label\">modifica note ed elenchi No Nonsense Notes</string>\n    <string name=\"permission_write_desc\">Permetti all\\'app di inserire, aggiornare ed eliminare note ed elenchi di No Nonsense Notes</string>\n    <string name=\"settings_list_dialog\">Seleziona un elenco</string>\n    <string name=\"settings_list\">Elenco</string>\n    <string name=\"notification_channel_name\">Promemoria delle note</string>\n    <string name=\"notification_channel_description\">Mostra il contenuto della nota quando arriva il momento segnato nel promemoria</string>\n    <string name=\"feature_is_WIP\">Ci stiamo ancora lavorando...</string>\n    <string name=\"permission_denied\">Annullato: non hai dato il permesso</string>\n    <string name=\"no_sync_method_chosen\">Non hai attivato la sincronizzazione</string>\n    <string name=\"file_picker_not_available\">L\\'app per gestire i file non è disponibile</string>\n    <string name=\"choose_backup_folder\">Scegli la cartella per i backup</string>\n    <string name=\"move\">Sposta</string>\n    <string name=\"move_to\">Sposta in</string>\n    <string name=\"moved_x_to_list\">Spostato %1$d in %2$s</string>\n    <string name=\"lock_note\">Blocca nota</string>\n    <string name=\"unlock_note\">Sblocca nota</string>\n    <string name=\"locked\">Bloccata</string>\n    <string name=\"unlocked\">Sbloccata</string>\n    <string name=\"password_required\">Password richiesta</string>\n    <string name=\"select_account\">Seleziona un account</string>\n    <string name=\"password\">Password</string>\n    <string name=\"password_set\">Password impostata</string>\n    <string name=\"password_cleared\">Password eliminata</string>\n    <string name=\"passwords_dont_match\">Le password non corrispondono</string>\n    <string name=\"password_incorrect\">Password errata</string>\n    <string name=\"enter_password\">Inserisci la password</string>\n    <string name=\"enter_new_password\">Inserisci la nuova password</string>\n    <string name=\"confirm_new_password\">Conferma la nuova password</string>\n    <string name=\"apply\">Applica</string>\n    <string name=\"clear_password\">Azzera password</string>\n    <string name=\"password_info\">Puoi impostare una password per bloccare le note. La protezione è solo per il contenuto, i titoli e le scadenze saranno comunque visibili. Le note non vengono cifrate.</string>\n    <string name=\"about\">Informazioni</string>\n    <string name=\"sync_failed\">Sincronizzazione non riuscita</string>\n    <string name=\"sync_login_failed\">Login non riuscito, non è possibile connettersi a Google Tasks</string>\n    <string name=\"completed\">Completata</string>\n    <string name=\"date_header_overdue\">Scadute</string>\n    <string name=\"date_header_today\">Oggi</string>\n    <string name=\"date_header_tomorrow\">Domani</string>\n    <string name=\"date_header_future\">Dopo</string>\n    <string name=\"date_header_none\">Nessuna data</string>\n    <string name=\"date_header_completed\">Completate</string>\n    <string name=\"please_select_note\">Prima seleziona o crea una nota</string>\n    <string name=\"please_create_note\">Devi creare una nota</string>\n    <string name=\"hide_checkbox\">Nascondi casella di spunta</string>\n    <string name=\"hide_checkbox_summary_on\">La casella di spunta verrà nascosta</string>\n    <string name=\"hide_checkbox_summary_off\">La casella di spunta verrà mostrata</string>\n    <string name=\"hide_date\">Nascondi la data di scadenza</string>\n    <string name=\"item_max_height\">Altezza massima della nota, in righe</string>\n    <string name=\"long_date_format\">Formato data estesa, per il pannello</string>\n    <string name=\"short_date_format\">Formato data compatta, per la lista</string>\n    <string name=\"select_date\">Seleziona la data</string>\n    <string name=\"localedefault\">Predefinita del sistema</string>\n    <string name=\"hide_header\">Nascondi intestazione widget</string>\n    <string name=\"transparency\">Trasparenza</string>\n    <string name=\"notes_shortcut\">Scorciatoia note</string>\n    <string name=\"shortcut_help1\">Se attivo, il collegamento creerà una nuova nota nell\\'elenco selezionato e aprirà l\\'editor. Altrimenti il collegamento aprirà l\\'elenco selezionato.</string>\n    <string name=\"loading_widget\">Caricamento widget…</string>\n    <string name=\"default_list\">Elenco predefinito</string>\n    <string name=\"please_type_before_reminder\">Inserisci del testo prima di impostare un promemoria</string>\n    <string name=\"notecopied_one\">Copiata una nota</string>\n    <string name=\"notecopied_other\">Copiate %d note</string>\n    <string name=\"notedeleted_one\">Eliminata una nota</string>\n    <string name=\"notedeleted_other\">Eliminate %d note</string>\n    <string name=\"selected_one\">Selezionata una nota</string>\n    <string name=\"selected_other\">Selezionate %d note</string>\n    <string name=\"background_sync\">Sincronizza in background</string>\n    <string name=\"background_sync_info\">Sincronizza ogni ora</string>\n    <string name=\"sync_on_change\">Sincronizza a ogni modifica</string>\n    <string name=\"sync_on_change_info\">Pianifica la sincronizzazione poco dopo la modifica della nota</string>\n    <string name=\"sync_on_start\">Sincronizza all\\'avvio dell\\'applicazione</string>\n    <string name=\"sync_on_start_info\">Avverrà solo all\\'avvio iniziale, non ad ogni ripristino</string>\n    <string name=\"silent\">Silenzioso</string>\n    <string name=\"sound\">Suono</string>\n    <string name=\"vibrate\">Vibrazione</string>\n    <string name=\"reminders\">Promemoria</string>\n    <string name=\"notification_prio_high\">Alta: in alto, espansa</string>\n    <string name=\"notification_prio_low\">Bassa: nascosta, minimizzata</string>\n    <string name=\"priority\">Priorità</string>\n    <string name=\"notifications\">Notifiche</string>\n    <string name=\"tasks\">Attività</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Limita alle attività in un elenco specifico</string>\n    <string name=\"dashclock_show_overdue_tasks\">Mostra attività scadute</string>\n    <string name=\"dashclock_overdue_tasks_show\">Le attività scadute verranno mostrate</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Le attività scadute verranno nascoste</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Impostazioni NoNonsense Notes</string>\n    <string name=\"dashclock_pref_header_general\">Generale</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limite per attività scadute al più tardi</string>\n    <string name=\"dashclock_today\">Oggi</string>\n    <string name=\"dashclock_tomorrow\">Domani</string>\n    <string name=\"dashclock_next7\">Prossimi 7 giorni</string>\n    <string name=\"dashclock_anytime\">Sempre</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Mostrerà più attività possibili</string>\n    <string name=\"dashclock_show_only_the_next_task\">Mostra solo la prossima attività</string>\n    <string name=\"dashclock_header_shown\">Il nome dell\\'elenco sarà mostrato in grassetto</string>\n    <string name=\"dashclock_first_task_shown\">Il titolo della prima attività sarà mostrato in grassetto</string>\n    <string name=\"dashclock_display_header\">Mostra il nome della lista come intestazione</string>\n    <string name=\"new_default_set\">Nuovo predefinito impostato</string>\n    <string name=\"lists\">Elenchi</string>\n    <string name=\"drag_to_timetravel\">Trascina per le versioni precedenti</string>\n    <string name=\"import_data_question\">Importare i dati?</string>\n    <string name=\"import_started\">Ripristino. Potrebbe volerci qualche secondo…</string>\n    <string name=\"imported_result\">Importate %1$d note in %2$d liste</string>\n    <string name=\"import_error\">C\\'è stato un problema: %s</string>\n    <string name=\"next_5_days\">Prossimi 5 giorni</string>\n    <string name=\"next_n_days\">Prossimi %d giorni</string>\n    <string name=\"this_week\">In settimana</string>\n    <string name=\"snooze\">Posticipa</string>\n    <string name=\"standard\">Predefinito</string>\n    <string name=\"all_tasks\">Tutte le attività</string>\n    <string name=\"bold\">Grassetto</string>\n    <string name=\"italic\">Corsivo</string>\n    <string name=\"normal\">Normale</string>\n    <string name=\"title_style\">Stile titolo</string>\n    <string name=\"title_font\">Carattere del titolo</string>\n    <string name=\"body_font\">Carattere della nota</string>\n    <string name=\"clickable_links\">Link cliccabili</string>\n    <string name=\"small\">Piccolo</string>\n    <string name=\"large\">Grande</string>\n    <string name=\"text_size\">Dimensione testo</string>\n    <string name=\"text\">Testo</string>\n    <string name=\"add_a_reminder\">Aggiungi promemoria</string>\n    <string name=\"medium\">Medio</string>\n    <string name=\"backup_import\">Importa il backup</string>\n    <string name=\"backup_export\">Esporta il backup</string>\n    <string name=\"backup_import_msg\">Provo a importare il backup da %1$s\\? Questo sovrascriverà tutte le note attuali.</string>\n    <string name=\"backup_export_msg\">Esportare tutte le note in %1$s?</string>\n    <string name=\"backup_import_success\">Backup importato correttamente</string>\n    <string name=\"backup_file_not_found\">File di backup NoNonsenseNotes_Backup.json non trovato</string>\n    <string name=\"backup_import_failed\">Impossibile leggere il file di backup NoNonsenseNotes_Backup.json</string>\n    <string name=\"backup_export_success\">Backup esportato correttamente</string>\n    <string name=\"backup_export_failed\">Impossibile scrivere sul file di backup</string>\n    <string name=\"sd_card\">Scheda SD</string>\n    <string name=\"sd_card_sync\">Sincronizzazione su scheda SD</string>\n    <string name=\"sd_card_summary\">Le note sono salvate anche sulla scheda SD. Le modifiche sono sincronizzate in entrambi i versi. Cancellare i file eliminerà anche le note nell\\'app!</string>\n    <string name=\"directory\">Cartella</string>\n    <string name=\"cannot_write_to_directory\">Impossibile scrivere sulla cartella</string>\n    <string name=\"libraries_used\">Librerie usate</string>\n    <string name=\"delete_completed_tasks_question\">Vuoi davvero eliminare tutte le note completate\\?</string>\n    <string name=\"navigation_drawer_open\">Apri il menù di navigazione</string>\n    <string name=\"navigation_drawer_close\">Chiudi il menù di navigazione</string>\n    <string name=\"use_exact_alarms\">Usa notifiche esatte</string>\n    <string name=\"exact_alarms_summary\">Le notifiche dei promemoria appariranno in modo più affidabile, ma l\\'app userà più batteria</string>\n    <string name=\"disable_battery_optimizations\">Disattiva otimizzazione batteria</string>\n    <string name=\"battery_optimizations_active\">L\\'uso di batteria è attualmente: ottimizzato</string>\n    <string name=\"battery_optimizations_inactive\">L\\'uso di batteria è: non ottimizzato</string>\n    <string name=\"allow_exact_reminders\">Consenti notifiche esatte</string>\n    <string name=\"allow_exact_reminders_summary\">Apri una pagina per consentire a quest\\'app di inviare notifiche in modo più affidabile. Serve solo da Android 12 Snow Cone in poi</string>\n    <string name=\"for_older_devices\">Per dispositivi android più vecchi</string>\n    <string name=\"overwritten_in_newer_systems\">Le impostazioni del canale di notifica, se disponibili, sovrascrivono queste</string>\n    <string name=\"prefs_improved_reliability\">Impostazioni per aumentare l\\'affidabilità</string>\n    <string name=\"notification_channel_settings\">Impostazioni del canale notifiche</string>\n    <string name=\"open_channel_settings_description\">Apri una pagina per modificare le impostazioni di notifica per quest\\'app. Queste hanno la precedenza su altre impostazioni</string>\n    <string name=\"account\">Profilo</string>\n    <string name=\"bigger_titles\">Titolo più grande</string>\n    <string name=\"bigger_titles_summary\">Il titolo è la prima linea</string>\n    <string name=\"sorting\">Ordine</string>\n    <string name=\"undo\">Annulla</string>\n    <string name=\"app_about_dev_team\">Creata da Jonas Kalderstam e mantenuta da Campello Manuel</string>\n    <string name=\"app_about_license\">Questo programma è fornito con la licenza GNU General Public License version 3, vedi http://www.gnu.org/licenses</string>\n    <string name=\"app_about_bugreports\">Segnala problemi qui https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"app_about_translations\">Aiutaci a tradurre l\\'app qui https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"app_about_git_repo\">Quest\\'app è open source e disponibile su https://github.com/spacecowboy/NotePad</string>\n    <string name=\"disable_android_hibernation_desc\">Rende l\\'app inaffidabile e può bloccare i promemoria. Apri questo, cerca qualcosa come \\\"sospendi se inutilizzata\\\" e disattivalo</string>\n    <string name=\"disable_android_hibernation\">Disattiva sospensione di Android</string>\n    <string name=\"msg_enable_notifications\">Vai in impostazioni, su Notifiche, per abilitare le notifiche</string>\n    <string name=\"notifications_enabled\">Le notifiche sono visibili</string>\n    <string name=\"notifications_visibility\">Visibilità delle notifiche</string>\n    <string name=\"notifications_blocked\">Le notifiche non sono visibili, quindi non vedrai i promemoria. Apri questo, trova sezione permessi ed abilita le notifiche per l\\'app</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"msg_hibernation_already_off\">La sospensione di Android è già spenta, non ti serve questo</string>\n    <string name=\"unavailable_chose_directory\">Non disponibile, prima devi scegliere una cartella</string>\n    <string name=\"editor\">Pagina di modifica</string>\n    <string name=\"not_selected_yet\">Non ancora selezionata</string>\n    <string name=\"directory_summary_msg\">Nuove restrizioni di Google costringono a salvare i file in %s\n\\nche puoi vedere con la tua app per gestire i file. Disinstallare l\\'app eliminerà tutti questi file. Per tenerti le note, usa i backup</string>\n    <string name=\"enable_sync\">Attiva la sincronizzazione</string>\n    <string name=\"enable_sync_desc\">Abilita le funzioni di sincronizzazione e consente di scegliere una sorgente con cui sincronizzarsi. Non riguarda i backup</string>\n    <string name=\"changelog_online\">Elenco modifiche (online)</string>\n    <string name=\"canceled_note_locked\">Annullato: la nota è bloccata</string>\n    <string name=\"order_notes_by\">Ordina le note per:</string>\n    <string name=\"show_elements_as\">Mostra gli elementi come:</string>\n    <string name=\"next_month\">Prossimo mese</string>\n    <string name=\"next_year\">Prossimo anno</string>\n    <string name=\"showcase_tutorial_title\">Troppo difficile\\?</string>\n    <string name=\"showcase_tutorial_description\">C\\'è un tutorial online, lo trovi nelle impostazioni</string>\n    <string name=\"welcome_note_row_3\">Quest\\'app ha un tutorial dettagliato. Puoi trovarlo alla pagina</string>\n    <string name=\"unsupported_readonly_file\">File in sola lettura non supportato: %s</string>\n    <string name=\"select_all\">Seleziona tutti</string>\n    <string name=\"welcome_note_row_2\">Apri questa nota per iniziare.</string>\n    <string name=\"tutorial_online\">Tutorial (online)</string>\n    <string name=\"welcome_note_title\">Benvenuto!</string>\n    <string name=\"version\">Versione: %s</string>\n    <string name=\"show_completed_notes\">Mostra le note completate</string>\n    <string name=\"app_about_donations\">Accettiamo donazioni. Per supportarci, cerca %1$s in %2$s</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-iw-rIL/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">פתקים</string>\n    <string name=\"title_create\">פתק חדש</string>\n    <string name=\"menu_delete\">מחק</string>\n    <string name=\"menu_deletelist\">מחק רשימה</string>\n    <string name=\"menu_sync\">רענן</string>\n    <string name=\"menu_share\">שתף</string>\n    <string name=\"menu_preferences\">הגדרות</string>\n    <string name=\"menu_createlist\">צור רשימה</string>\n    <string name=\"menu_clearcompleted\">נקה משימות שהושלמו</string>\n    <string name=\"menu_setdefaultlist\">קבע כרשימת ברירת מחדל</string>\n    <string name=\"editor_due_date_hint\">תאריך יעד</string>\n    <string name=\"editor_title_hint\">כותרת</string>\n    <string name=\"editor_note_hint\">פתק</string>\n    <string name=\"resolve_edit\">ערוך פתק</string>\n    <string name=\"search_hint\">חיפוש</string>\n    <string name=\"settings_theme\">ערכת נושא נוכחית</string>\n    <string name=\"settings_theme_dialog\">השתמש בערכת נושא</string>\n    <string name=\"settings_summary_theme_dark\">כהה</string>\n    <string name=\"settings_summary_theme_light\">בהיר</string>\n    <string name=\"settings_cat_appearance\">מראה</string>\n    <string name=\"preference_preview_text\">כך הטקסט יופיע בעת עריכה</string>\n    <string name=\"settings_account_title\">בחר חשבון</string>\n    <string name=\"settings_account_summary\">בחר להחלפת חשבון</string>\n    <string name=\"settings_cat_syncing\">סינכרון</string>\n    <string name=\"show_from_all_lists\">כל הרשימות</string>\n    <string name=\"deleted\">נמחק</string>\n    <string name=\"permission_read_label\">קרא פתקים ורשימות</string>\n    <string name=\"permission_read_desc\">מאפשר ליישום לקרוא את הפתקים והרשימות הקשורות</string>\n    <string name=\"permission_write_label\">ערוך פתקים ורשימות</string>\n    <string name=\"permission_write_desc\">מאפשר ליישום להוסיף, לעדכן ולמחוק פתקים ורשימות</string>\n    <string name=\"settings_list_dialog\">בחר רשימה</string>\n    <string name=\"settings_list\">רשימה</string>\n    <!--\n        Use few if language need it. In English is the same.\n        <item quantity=\"few\">Copied 2 notes.</item>\n    -->\n    <string name=\"lock_note\">נעל פתק</string>\n    <string name=\"unlock_note\">בטל נעילת פתק</string>\n    <string name=\"locked\">נעול</string>\n    <string name=\"unlocked\">נעילה מבוטלת</string>\n    <string name=\"password_required\">נדרשת סיסמא</string>\n    <string name=\"select_account\">בחר חשבון</string>\n    <string name=\"password\">סיסמא</string>\n    <string name=\"password_set\">קביעת סיסמא</string>\n    <string name=\"password_cleared\">סיסמא בוטלה</string>\n    <string name=\"passwords_dont_match\">הסיסמאות לא תואמות</string>\n    <string name=\"enter_password\">הזן סיסמה</string>\n    <string name=\"enter_new_password\">הזן סיסמא חדשה</string>\n    <string name=\"confirm_new_password\">הקש שוב את הסיסמא החדשה</string>\n    <string name=\"apply\">החל</string>\n    <string name=\"clear_password\">בטל סיסמא</string>\n    <string name=\"password_info\">על ידי הגדרת סיסמה, תתבקש להזין את הסיסמה כדי להציג הערות נעולות. פתקים נעולים יציגו רק את הכותרת שלהם ואת תאריך היעד. פתקים נעולים עדיין ניתן לראות ב-Gmail וביומן Google. אם שכחת את הסיסמה שלך, סנכרן עם Google והתקן מחדש את היישום.</string>\n    <string name=\"about\">אודות</string>\n    <!-- settings string keys -->\n    <!-- \"localtime\" is replaced at runtime with 12/24 hour clock -->\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"title_create\">新しいノート</string>\n    <string name=\"timemachine\">タイムマシン</string>\n    <string name=\"time\">時間</string>\n    <string name=\"done\">完了</string>\n    <string name=\"saved\">保存済み</string>\n    <string name=\"menu_save_and_add\">デリート</string>\n    <string name=\"menu_delete\">削除</string>\n    <string name=\"menu_deletelist\">リストの削除</string>\n    <string name=\"menu_sync\">更新</string>\n    <string name=\"menu_share\">共有</string>\n    <string name=\"menu_preferences\">設定</string>\n    <string name=\"menu_createlist\">リストの作成</string>\n    <string name=\"menu_clearcompleted\">完了したノートの削除</string>\n    <string name=\"menu_setdefaultlist\">デフォルトのリスト</string>\n    <string name=\"menu_managelists\">リストの管理</string>\n    <string name=\"add_item\">アイテムを追加</string>\n    <string name=\"delete_question\">削除しますか？</string>\n    <string name=\"delete_items_message\">これらのアイテムを削除しますか？</string>\n    <string name=\"delete_item_message\">このアイテムを削除しますか？</string>\n    <string name=\"delete_list_message\">リストを削除しますか？</string>\n    <string name=\"editor_due_date_hint\">期限</string>\n    <string name=\"editor_title_hint\">タイトル</string>\n    <string name=\"editor_note_hint\">ノート</string>\n    <string name=\"resolve_edit\">ノートの編集</string>\n    <string name=\"default_style\">デフォルトスタイル</string>\n    <string name=\"show_items_as_tasks\">アイテムをタスクとして表示</string>\n    <string name=\"show_items_as_notes\">アイテムをノートとして表示</string>\n    <string name=\"sort_list_default\">デフォルト並び順</string>\n    <string name=\"sort_list_alphabetical\">アルファベット順</string>\n    <string name=\"sort_list_due\">期限順</string>\n    <string name=\"sort_list_updated\">更新日時順</string>\n    <string name=\"sort_list_manual\">手動</string>\n    <string name=\"search_description\">ノートやタスク</string>\n    <string name=\"archive\">アーカイブ</string>\n    <string name=\"restore\">復元</string>\n    <string name=\"restore_to\">...へ復元</string>\n    <string name=\"search_hint\">検索</string>\n    <string name=\"settings_theme\">現在のテーマ</string>\n    <string name=\"settings_theme_dialog\">テーマの選択</string>\n    <string name=\"settings_lang\">言語</string>\n    <string name=\"settings_summary_theme_black\">ブラック</string>\n    <string name=\"settings_summary_theme_dark\">ダーク</string>\n    <string name=\"settings_summary_theme_light\">ライト</string>\n    <string name=\"settings_summary_theme_classic\">クラシック</string>\n    <string name=\"settings_cat_appearance\">外観</string>\n    <string name=\"preference_preview_text\">ここで実際の見た目を確認することが出来ます</string>\n    <string name=\"settings_account_title\">アカウントを選択</string>\n    <string name=\"settings_account_summary\">タップしてアカウントを切り替える</string>\n    <string name=\"settings_cat_syncing\">同期</string>\n    <string name=\"show_from_all_lists\">すべてのノート</string>\n    <string name=\"deleted\">削除済み</string>\n    <string name=\"repeat\">繰り返し</string>\n    <string name=\"once\">一度</string>\n    <string name=\"always\">常に</string>\n    <string name=\"permission_read_label\">ノートやリストを読み込む</string>\n    <string name=\"permission_read_desc\">アプリケーションがノートとリストを読み取ることを許可します</string>\n    <string name=\"permission_write_label\">ノートやリストを変更</string>\n    <string name=\"permission_write_desc\">アプリケーションがノートやリストの挿入、更新、および削除することを許可します</string>\n    <string name=\"settings_list_dialog\">リストの選択</string>\n    <string name=\"settings_list\">リスト</string>\n    <string name=\"drag_to_timetravel\">ドラッグして遡る</string>\n    <string name=\"import_data_question\">データをインポートしますか？</string>\n    <string name=\"import_started\">データをインポート中…</string>\n    <string name=\"imported_result\">%1$d個のノートを%2$dリストへインポートしました</string>\n    <string name=\"import_error\">エラーが発生しました： %s</string>\n    <string name=\"move\">移動</string>\n    <string name=\"move_to\">...へ移動</string>\n    <string name=\"moved_x_to_list\">%1$d を %2$s へ移動</string>\n    <string name=\"lock_note\">ノートのロック</string>\n    <string name=\"unlock_note\">ロックの解除</string>\n    <string name=\"locked\">ロック済み</string>\n    <string name=\"unlocked\">未ロック</string>\n    <string name=\"password_required\">パスワードが必要です</string>\n    <string name=\"select_account\">アカウントを選択する</string>\n    <string name=\"password\">パスワード</string>\n    <string name=\"password_set\">パスワードを設定しました</string>\n    <string name=\"password_cleared\">パスワードをクリアしました</string>\n    <string name=\"passwords_dont_match\">パスワードが一致しません</string>\n    <string name=\"password_incorrect\">パスワードが違います</string>\n    <string name=\"enter_password\">パスワードを入力</string>\n    <string name=\"enter_new_password\">新しいパスワードを入力</string>\n    <string name=\"confirm_new_password\">新しいパスワードの確認</string>\n    <string name=\"apply\">適用</string>\n    <string name=\"clear_password\">パスワードをクリア</string>\n    <string name=\"password_info\">パスワードを設定すると、ロックされたノートを表示するのにパスワードを要求するようになります。ロックされたノートでは、タイトルや期限は保護されずに、ノート欄を保護します。ノートはGmailやGoogleカレンダーで表示する為、ノートを暗号化することはしません。パスワードを忘れた場合は、Googleと同期してから、アプリケーションを再インストールしてください。</string>\n    <string name=\"about\">このアプリについて</string>\n    <string name=\"sync_failed\">同期失敗</string>\n    <string name=\"sync_login_failed\">ログイン失敗。Google Tasksに接続できませんでした</string>\n    <string name=\"completed\">完了</string>\n    <string name=\"date_header_overdue\">期限切れ</string>\n    <string name=\"date_header_today\">今日</string>\n    <string name=\"date_header_tomorrow\">明日</string>\n    <string name=\"date_header_future\">後日</string>\n    <string name=\"date_header_none\">期限なし</string>\n    <string name=\"date_header_completed\">完了</string>\n    <string name=\"please_select_note\">ノートを選択、または作成してください</string>\n    <string name=\"please_create_note\">ノートを作成してください</string>\n    <string name=\"hide_checkbox\">チェックボックスを表示しない</string>\n    <string name=\"hide_checkbox_summary_on\">現在の設定：非表示</string>\n    <string name=\"hide_checkbox_summary_off\">現在の設定：表示</string>\n    <string name=\"hide_date\">期限を表示しない</string>\n    <string name=\"item_max_height\">ノート/タスクの最大高さ</string>\n    <string name=\"long_date_format\">詳細日付書式</string>\n    <string name=\"short_date_format\">短縮日付書式</string>\n    <string name=\"select_date\">日時を選択</string>\n    <string name=\"localedefault\">端末の設定を使用する</string>\n    <string name=\"hide_header\">ヘッダを表示しない</string>\n    <string name=\"transparency\">透明度</string>\n    <string name=\"notes_shortcut\">Notesショートカット</string>\n    <string name=\"shortcut_help1\">チェックすると、ショートカットタップ時に選択したリストに新規ノートを作成し、エディタを開きます。チェックを外すと、ショートカットタップ時に選択したリストを開きます。</string>\n    <string name=\"loading_widget\">ウィジェットを読み込み中…</string>\n    <string name=\"default_list\">デフォルトリスト</string>\n    <string name=\"please_type_before_reminder\">リマインダーを設定する前にテキストを入力してください</string>\n    <string name=\"notecopied_one\">ノートをコピーしました</string>\n    <string name=\"notecopied_other\">%d 個のノートをコピーしました</string>\n    <string name=\"notedeleted_one\">ノートを削除しました</string>\n    <string name=\"notedeleted_other\">%d 個のノートを削除しました</string>\n    <string name=\"selected_one\">ノートを選択</string>\n    <string name=\"selected_other\">%d 個のノートを選択</string>\n    <string name=\"background_sync\">バックグラウンド同期</string>\n    <string name=\"background_sync_info\">1時間毎に同期されます</string>\n    <string name=\"sync_on_change\">変更後すぐに同期</string>\n    <string name=\"sync_on_change_info\">ノートが変更されるとすぐに同期されます</string>\n    <string name=\"sync_on_start\">アプリ起動時に同期</string>\n    <string name=\"sync_on_start_info\">アプリが復帰したときではなく、起動時に同期されます</string>\n    <string name=\"snooze\">スヌーズ</string>\n    <string name=\"silent\">サイレント</string>\n    <string name=\"sound\">通知音</string>\n    <string name=\"vibrate\">バイブレーション</string>\n    <string name=\"reminders\">リマインダー</string>\n    <string name=\"standard\">普通</string>\n    <string name=\"notification_prio_high\">高：通知領域の先頭、拡張</string>\n    <string name=\"notification_prio_low\">低：隠す、縮小</string>\n    <string name=\"priority\">優先度</string>\n    <string name=\"notifications\">通知</string>\n    <string name=\"tasks\">タスク</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">表示するタスクを限定</string>\n    <string name=\"dashclock_show_overdue_tasks\">期限切れタスクを表示</string>\n    <string name=\"dashclock_overdue_tasks_show\">期限切れタスクが表示されます</string>\n    <string name=\"dashclock_overdue_tasks_hide\">期限切れタスクが表示されません</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense Notes設定</string>\n    <string name=\"dashclock_pref_header_general\">一般</string>\n    <string name=\"dashclock_due_upper_limit_title\">タスクの期限で限定</string>\n    <string name=\"dashclock_today\">今日</string>\n    <string name=\"dashclock_tomorrow\">明日</string>\n    <string name=\"dashclock_next7\">1週間以内</string>\n    <string name=\"dashclock_anytime\">無制限</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">可能な限り表示します</string>\n    <string name=\"dashclock_show_only_the_next_task\">次のタスクのみ表示</string>\n    <string name=\"dashclock_header_shown\">リスト名が太字で表示されます</string>\n    <string name=\"dashclock_first_task_shown\">最初のタスクが太字で表示されます</string>\n    <string name=\"dashclock_display_header\">リスト名を表示</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">노트</string>\n    <string name=\"title_create\">새 노트</string>\n    <string name=\"timemachine\">타임머신</string>\n    <string name=\"time\">시간</string>\n    <string name=\"done\">완료</string>\n    <string name=\"saved\">저장 완료</string>\n    <string name=\"menu_save_and_add\">저장하고 메모 추가</string>\n    <string name=\"menu_delete\">지우기</string>\n    <string name=\"menu_deletelist\">목록 지우기</string>\n    <string name=\"menu_sync\">새로고침</string>\n    <string name=\"menu_share\">공유</string>\n    <string name=\"menu_preferences\">설정</string>\n    <string name=\"menu_createlist\">목록 만들기</string>\n    <string name=\"menu_clearcompleted\">완료된 노트 지우기</string>\n    <string name=\"menu_setdefaultlist\">기본 목록으로 설정</string>\n    <string name=\"new_default_set\">새로운 기본 세트</string>\n    <string name=\"menu_managelists\">목록 관리</string>\n    <string name=\"add_item\">항목 추가</string>\n    <string name=\"delete_question\">삭제?</string>\n    <string name=\"editor_due_date_hint\">목표 날짜</string>\n    <string name=\"editor_title_hint\">제목</string>\n    <string name=\"editor_note_hint\">노트</string>\n    <string name=\"resolve_edit\">노트 고치기</string>\n    <string name=\"search_hint\">찾기</string>\n    <string name=\"settings_theme\">현재 테마</string>\n    <string name=\"settings_theme_dialog\">테마 사용하기</string>\n    <string name=\"settings_lang\">언어</string>\n    <string name=\"settings_summary_theme_black\">검정</string>\n    <string name=\"settings_summary_theme_dark\">어두움</string>\n    <string name=\"settings_summary_theme_light\">밝음</string>\n    <string name=\"settings_cat_appearance\">모양</string>\n    <string name=\"preference_preview_text\">편집기 글꼴이 이렇게 보입니다</string>\n    <string name=\"settings_account_title\">계정 선택</string>\n    <string name=\"settings_account_summary\">계정 바꾸기</string>\n    <string name=\"settings_cat_syncing\">동기화</string>\n    <string name=\"show_from_all_lists\">모든 목록</string>\n    <string name=\"deleted\">삭제됨</string>\n    <string name=\"permission_read_label\">노트와 목록을 읽기</string>\n    <string name=\"permission_read_desc\">애플리케이션이 사용자의 노트와 목록을 읽게 합니다</string>\n    <string name=\"permission_write_label\">노트와 목록을 수정</string>\n    <string name=\"permission_write_desc\">애플리케이션이 노트와 목록을 추가, 수정, 삭제하게 합니다</string>\n    <string name=\"settings_list_dialog\">목록 선택</string>\n    <string name=\"settings_list\">목록</string>\n    <!--\n        Use few if language need it. In English is the same.\n        <item quantity=\"few\">Copied 2 notes.</item>\n    -->\n    <string name=\"lock_note\">노트 잠금</string>\n    <string name=\"unlock_note\">노트 잠금 풀기</string>\n    <string name=\"locked\">잠김</string>\n    <string name=\"unlocked\">잠금 풀림</string>\n    <string name=\"password_required\">비밀번호 필요</string>\n    <string name=\"select_account\">계정 선택</string>\n    <string name=\"password\">비밀번호</string>\n    <string name=\"password_set\">비밀번호 선택됨</string>\n    <string name=\"password_cleared\">비밀번호 지워짐</string>\n    <string name=\"passwords_dont_match\">비밀번호 같지 않음</string>\n    <string name=\"password_incorrect\">비밀번호 틀림</string>\n    <string name=\"enter_password\">비밀번호 입력</string>\n    <string name=\"enter_new_password\">새 비밀번호 입력</string>\n    <string name=\"confirm_new_password\">새 비밀번호 확인</string>\n    <string name=\"apply\">적용</string>\n    <string name=\"clear_password\">비밀번호 지우기</string>\n    <string name=\"password_info\">비밀번호를 설정하면, 잠근 노트를 열기 위해 비밀번호를 입력해야 합니다. 노트를 잠그면 내용은 숨기고 제목과 목표 날짜는 비밀번호 없이 볼 수 있습니다. 내용이 암호화되지 않으므로 Gmail이나 Google Calendar에서 볼 수 있습니다. 비밀번호를 잊으면, Google과 동기화 후 애플리케이션을 다시 설치하십시오.</string>\n    <string name=\"about\">정보</string>\n    <string name=\"sync_failed\">동기화 실패</string>\n    <string name=\"sync_login_failed\">로그인을 실패하여 Google Tasks에 연결할 수 없음</string>\n    <string name=\"completed\">완료됨</string>\n    <string name=\"date_header_overdue\">목표 날짜 지남</string>\n    <string name=\"date_header_today\">오늘</string>\n    <string name=\"date_header_tomorrow\">내일</string>\n    <string name=\"date_header_future\">나중</string>\n    <string name=\"date_header_none\">날짜 없음</string>\n    <string name=\"date_header_completed\">완료됨</string>\n    <string name=\"please_select_note\">노트를 만들거나 선택하세요</string>\n    <string name=\"please_create_note\">노트를 만드세요</string>\n    <string name=\"hide_checkbox\">체크 표시 숨기기</string>\n    <string name=\"hide_checkbox_summary_on\">체크 표시 보이지 않음</string>\n    <string name=\"hide_checkbox_summary_off\">체크 표시 보임</string>\n    <string name=\"hide_date\">목표 날짜 숨기기</string>\n    <string name=\"localedefault\">언어 설정 따르기</string>\n    <!-- settings string keys -->\n    <string name=\"hide_header\">위젯 제목 숨기기</string>\n    <string name=\"shortcut_help1\">체크하면, 바로가기를 누르면 선택한 리스트에 새 노트를 만들고 편집기를 엽니다. 체크하지 않으면, 선택한 리스트만 보여줍니다.</string>\n    <string name=\"loading_widget\">위젯 읽고 있음…</string>\n    <string name=\"notecopied_one\">노트 복사됨</string>\n    <string name=\"notecopied_other\">노트 %d개 복사됨</string>\n    <string name=\"notedeleted_one\">노트 지워짐</string>\n    <string name=\"notedeleted_other\">노트 %d개 지워짐</string>\n    <string name=\"selected_one\">노트 선택됨</string>\n    <string name=\"selected_other\">노트 %d개 선택됨</string>\n    <string name=\"background_sync\">백그라운드 동기화</string>\n    <string name=\"background_sync_info\">한 시간마다 동기화</string>\n    <string name=\"sync_on_change\">고칠 때마다 동기화</string>\n    <string name=\"sync_on_change_info\">노트를 고치고 나면 동기화 되도록 설정</string>\n    <string name=\"sync_on_start\">애플리케이션 시작할 때마다 동기화하기</string>\n    <string name=\"sync_on_start_info\">애플리케이션을 \\'새로\\' 시작할 때에만 동기화됨</string>\n    <string name=\"silent\">조용히</string>\n    <string name=\"sound\">소리</string>\n    <string name=\"vibrate\">진동</string>\n    <string name=\"reminders\">알림이</string>\n    <string name=\"standard\">표준</string>\n    <string name=\"notification_prio_high\">상위: 맨 위에서 확장</string>\n    <string name=\"notification_prio_low\">하위: 숨김</string>\n    <string name=\"priority\">우선순위</string>\n    <string name=\"notifications\">알림</string>\n    <!-- \"localtime\" is replaced at runtime with 12/24 hour clock -->\n</resources>"
  },
  {
    "path": "app/src/main/res/values-land/constants.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<integer name=\"layout_best_orientation\">@integer/layout_horizontal</integer>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-land/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<!-- Changes depending on screen values -->\n\t<dimen name=\"widget_conf_preview_height\">@dimen/layout_match_parent</dimen>\n\t<dimen name=\"widget_conf_preview_width\">@dimen/widget_conf_preview_land_width</dimen>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-nb-rNO/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"snooze\">Slumre</string>\n    <string name=\"silent\">Lydløs</string>\n    <string name=\"sound\">Lyd</string>\n    <string name=\"vibrate\">Vibrering</string>\n    <string name=\"notifications\">Merknader</string>\n    <string name=\"reminders\">Påminnelser</string>\n    <string name=\"dashclock_pref_header_general\">Generelt</string>\n    <string name=\"dashclock_today\">I dag</string>\n    <string name=\"dashclock_tomorrow\">I morgen</string>\n    <string name=\"dashclock_next7\">De neste 7 dagene</string>\n    <string name=\"text_size\">Tekststørrelse</string>\n    <string name=\"localedefault\">Enhetsforvalg</string>\n    <string name=\"menu_sync\">Synkroniser</string>\n    <string name=\"delete_question\">Slett\\?</string>\n    <string name=\"done\">Ferdig</string>\n    <string name=\"saved\">Lagret</string>\n    <string name=\"menu_save_and_add\">Lagre og legg til notat</string>\n    <string name=\"menu_share\">Del</string>\n    <string name=\"menu_preferences\">Innstillinger</string>\n    <string name=\"menu_createlist\">Start ny liste</string>\n    <string name=\"menu_clearcompleted\">Fjern fullførte</string>\n    <string name=\"menu_managelists\">Rediger lister</string>\n    <string name=\"delete_items_message\">Slett disse elementene\\?</string>\n    <string name=\"new_default_set\">Nytt forvalg satt</string>\n    <string name=\"editor_due_date_hint\">Forfallsdato</string>\n    <string name=\"editor_title_hint\">Navn</string>\n    <string name=\"show_items_as_tasks\">Vis elementer som gjøremål</string>\n    <string name=\"show_items_as_notes\">Vis elementer som notater</string>\n    <string name=\"sort_list_default\">Forvalgt sorteringsrekkefølge</string>\n    <string name=\"sort_list_alphabetical\">A-Å</string>\n    <string name=\"restore\">Gjenopprett</string>\n    <string name=\"settings_theme\">Nåværende drakt</string>\n    <string name=\"sort_list_due\">Fristdato</string>\n    <string name=\"sort_list_manual\">Manuelt</string>\n    <string name=\"search_description\">Notater og gjøremål</string>\n    <string name=\"settings_summary_theme_black\">Svart</string>\n    <string name=\"settings_summary_theme_dark\">Mørk</string>\n    <string name=\"preference_preview_text\">Tekst i tekstbehandleren vil fremtre slik</string>\n    <string name=\"lists\">Lister</string>\n    <string name=\"once\">Én gang</string>\n    <string name=\"always\">Alltid</string>\n    <string name=\"import_data_question\">Importer data\\?</string>\n    <string name=\"navigation_drawer_close\">Lukk navigasjonsskuff</string>\n    <string name=\"settings_list_dialog\">Velg en liste</string>\n    <string name=\"import_started\">Importerer data …</string>\n    <string name=\"navigation_drawer_open\">Åpne navigasjonsskuff</string>\n    <string name=\"feature_is_WIP\">Denne funksjonen er under konstruksjon.</string>\n    <string name=\"permission_denied\">Kan ikke fortsette. du har ikke innvilget tilgangen</string>\n    <string name=\"choose_backup_folder\">Valgt sikkerhetskopimappe</string>\n    <string name=\"for_older_devices\">For eldre Android-enheter</string>\n    <string name=\"app_about_bugreports\">Rapporter feil og problemer på https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"moved_x_to_list\">Flyttet %1$d til %2$s</string>\n    <string name=\"account\">Konto</string>\n    <string name=\"unlock_note\">Lås opp notat</string>\n    <string name=\"locked\">Låst</string>\n    <string name=\"password_set\">Passord satt</string>\n    <string name=\"passwords_dont_match\">Passordene samsvarer ikke</string>\n    <string name=\"password_incorrect\">Feil passord</string>\n    <string name=\"enter_password\">Skriv inn passord</string>\n    <string name=\"confirm_new_password\">Bekreft nytt passord</string>\n    <string name=\"apply\">Bruk</string>\n    <string name=\"about\">Om</string>\n    <string name=\"date_header_future\">Senere</string>\n    <string name=\"date_header_none\">Ingen dato</string>\n    <string name=\"next_5_days\">De neste 5 dagene</string>\n    <string name=\"next_n_days\">De neste %d dagene</string>\n    <string name=\"please_select_note\">Velg eller opprett et notat</string>\n    <string name=\"hide_date\">Skjul fristdato</string>\n    <string name=\"long_date_format\">Langt datoformat</string>\n    <string name=\"short_date_format\">Kort datoformat</string>\n    <string name=\"transparency\">Gjennomsiktighet</string>\n    <string name=\"notes_shortcut\">Notatsnarvei</string>\n    <string name=\"loading_widget\">Laster inn miniprogram …</string>\n    <string name=\"notecopied_one\">Kopierte 1 notat</string>\n    <string name=\"notecopied_other\">Kopierte %d notater</string>\n    <string name=\"notedeleted_other\">Slettet %d notater</string>\n    <string name=\"notedeleted_one\">Slettet 1 notat</string>\n    <string name=\"priority\">Prioritet</string>\n    <string name=\"all_tasks\">Alle gjøremål</string>\n    <string name=\"dashclock_anytime\">Når som helst</string>\n    <string name=\"clickable_links\">Klikkbare lenker</string>\n    <string name=\"medium\">Middels</string>\n    <string name=\"large\">Store</string>\n    <string name=\"add_a_reminder\">Legg til påminnelse</string>\n    <string name=\"backup_import\">Importer sikkerhetskopi</string>\n    <string name=\"backup_file_not_found\">Fant ikke sikkerhetskopifilen</string>\n    <string name=\"backup_export_success\">Sikkerhetskopi eksportert</string>\n    <string name=\"sd_card\">SD-kort</string>\n    <string name=\"sd_card_sync\">Synkronisering av SD-kort</string>\n    <string name=\"directory\">Mappe</string>\n    <string name=\"cannot_write_to_directory\">Kan ikke skrive til mappe</string>\n    <string name=\"bigger_titles_summary\">Navnet er første linje</string>\n    <string name=\"sorting\">Sortering</string>\n    <string name=\"title_create\">Nytt notat</string>\n    <string name=\"timemachine\">Tidsmaskin</string>\n    <string name=\"undo\">Angre</string>\n    <string name=\"delete_list_message\">Slett denne listen\\?</string>\n    <string name=\"app_name_short\">Notater</string>\n    <string name=\"menu_setdefaultlist\">Sett som forvalgt liste</string>\n    <string name=\"add_item\">Legg til element</string>\n    <string name=\"time\">Tid</string>\n    <string name=\"menu_delete\">Slett</string>\n    <string name=\"menu_deletelist\">Slett liste</string>\n    <string name=\"delete_item_message\">Slett dette elementet\\?</string>\n    <string name=\"delete_completed_tasks_question\">Slett alle fullførte gjøremål\\?</string>\n    <string name=\"editor_note_hint\">Notat</string>\n    <string name=\"resolve_edit\">Rediger notat</string>\n    <string name=\"default_style\">Forvalgt stil</string>\n    <string name=\"restore_to\">Gjenopprett til</string>\n    <string name=\"search_hint\">Søk</string>\n    <string name=\"settings_theme_dialog\">Ifør drakt</string>\n    <string name=\"settings_account_summary\">Trykk for å bytte konto</string>\n    <string name=\"settings_lang\">Språk</string>\n    <string name=\"settings_summary_theme_classic\">Klassisk</string>\n    <string name=\"settings_summary_theme_light\">Lys</string>\n    <string name=\"settings_cat_appearance\">Utseende</string>\n    <string name=\"settings_account_title\">Velg en konto</string>\n    <string name=\"notification_channel_name\">Påminnelser for notater</string>\n    <string name=\"settings_cat_syncing\">Synkronisering</string>\n    <string name=\"show_from_all_lists\">Alle lister</string>\n    <string name=\"deleted\">Slettet</string>\n    <string name=\"repeat\">Gjenta</string>\n    <string name=\"settings_list\">Liste</string>\n    <string name=\"move_to\">Flytt til</string>\n    <string name=\"import_error\">Noe gikk galt: %s</string>\n    <string name=\"allow_exact_reminders\">Tillat nøyaktige påminnelser</string>\n    <string name=\"app_about_translations\">Bistå oversettelsen på https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"move\">Flytt</string>\n    <string name=\"lock_note\">Lås notat</string>\n    <string name=\"unlocked\">Opplåst</string>\n    <string name=\"this_week\">Denne uken</string>\n    <string name=\"password_required\">Passord kreves</string>\n    <string name=\"select_account\">Velg en konto</string>\n    <string name=\"password_cleared\">Passord fjernet</string>\n    <string name=\"completed\">Fullført</string>\n    <string name=\"date_header_tomorrow\">I morgen</string>\n    <string name=\"tasks\">Gjøremål</string>\n    <string name=\"enter_new_password\">Skriv inn nytt passord</string>\n    <string name=\"clear_password\">Fjern passord</string>\n    <string name=\"sync_failed\">Synkronisering mislyktes</string>\n    <string name=\"date_header_today\">I dag</string>\n    <string name=\"date_header_completed\">Fullført</string>\n    <string name=\"please_create_note\">Opprett et notat</string>\n    <string name=\"select_date\">Velg dato</string>\n    <string name=\"default_list\">Forvalgt liste</string>\n    <string name=\"archive\">Arkiv</string>\n    <string name=\"sort_list_updated\">Sist oppdatert</string>\n    <string name=\"permission_write_label\">endre notater og lister</string>\n    <string name=\"permission_write_desc\">Tillater programmet å sette inn, oppdatere, og slette notater og lister i No Nonsense Notes</string>\n    <string name=\"libraries_used\">Bibliotek</string>\n    <string name=\"imported_result\">Importerte %1$d notater inn i %2$d lister</string>\n    <string name=\"no_sync_method_chosen\">Du har ikke valgt en synkroniseringsmetode</string>\n    <string name=\"file_picker_not_available\">Filutvelgeren virker ikke</string>\n    <string name=\"app_about_dev_team\">Laget av Jonas Kalderstam og vedlikeholdt av Campello Manuel</string>\n    <string name=\"disable_battery_optimizations\">Skru av batterioptimaliseringer</string>\n    <string name=\"app_about_git_repo\">Gemenfrihetslig programvare, tilgjengelig på https://github.com/spacecowboy/NotePad</string>\n    <string name=\"selected_one\">1 notat valgt</string>\n    <string name=\"selected_other\">%d notater valgt</string>\n    <string name=\"title_style\">Navnestil</string>\n    <string name=\"backup\">Sikkerhetskopiering</string>\n    <string name=\"backup_import_success\">Sikkerhetskopi importert</string>\n    <string name=\"title_font\">Skrift for navn</string>\n    <string name=\"small\">Små</string>\n    <string name=\"text\">Tekst</string>\n    <string name=\"backup_export_failed\">Kunne ikke skrive til sikkerhetskopifil</string>\n    <string name=\"backup_import_failed\">Klarte ikke å lese sikkerhetskopifilen</string>\n    <string name=\"body_font\">Skrift for brødtekst</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Vis så mange som mulig</string>\n    <string name=\"dashclock_display_header\">Vis listenavn</string>\n    <string name=\"italic\">Kursiv</string>\n    <string name=\"dashclock_show_only_the_next_task\">Vis kun neste gjøremål</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"backup_export\">Eksporter sikkerhetskopi</string>\n    <string name=\"battery_optimizations_active\">Batterioptimalisering påskrudd</string>\n    <string name=\"notification_channel_settings\">Innstillinger for merknadskanal</string>\n    <string name=\"battery_optimizations_inactive\">Batterioptimalisering avskrudd</string>\n    <string name=\"background_sync\">Bakgrunnssynkronisering</string>\n    <string name=\"background_sync_info\">Synkroniserer én gang i timen</string>\n    <string name=\"sync_on_change\">Synkroniser ved endringer</string>\n    <string name=\"sync_on_start\">Synkroniser ved programstart</string>\n    <string name=\"sync_on_start_info\">Begrenser synkronisering til hvert femte minutt</string>\n    <string name=\"notification_prio_low\">Lav: Skjult, minimert</string>\n    <string name=\"bigger_titles\">Større navn</string>\n    <string name=\"please_type_before_reminder\">Skriv litt tekst før du legger til påminnelse</string>\n    <string name=\"notification_prio_high\">Høy: Øverst, utvidet</string>\n    <string name=\"permission_read_label\">lesing av notater og lister</string>\n    <string name=\"drag_to_timetravel\">Dra for tidsreise</string>\n    <string name=\"date_header_overdue\">Forfalt</string>\n    <string name=\"hide_checkbox\">Skjul avkryssningsboks</string>\n    <string name=\"hide_checkbox_summary_off\">Viser avkryssningsboksen</string>\n    <string name=\"item_max_height\">Maks høyde for notater/gjøremål, i rader</string>\n    <string name=\"hide_header\">Skjul hele miniprogramsnavnet</string>\n    <string name=\"hide_checkbox_summary_on\">Skjuler avkryssningsboksen</string>\n    <string name=\"permission_read_desc\">Lar programmet lese notatene dine og relaterte lister i No Nonsense Notes</string>\n    <string name=\"notification_channel_description\">Viser notatets innhold ved påminnelsestiden</string>\n    <string name=\"use_exact_alarms\">Nøyaktige alarmer</string>\n    <string name=\"overwritten_in_newer_systems\">Kan oppheves av evt. innstillinger for merknadskanal</string>\n    <string name=\"password_info\">Ethvert notat kan låses, noe som begrenser visning og endring av det. Det forblir tilgjengelige på andre enheter.</string>\n    <string name=\"sync_login_failed\">Kunne ikke koble til Google Tasks uten innlogging</string>\n    <string name=\"shortcut_help1\">Får snarveien til å åpne nytt notat i valgt liste med åpen tekstredigerer.</string>\n    <string name=\"sync_on_change_info\">Planlegger synkronisering rett etter endring av notat</string>\n    <string name=\"standard\">Standard</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Begrens til gjøremål i</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Innstillinger</string>\n    <string name=\"dashclock_due_upper_limit_title\">Begrens til gjøremål med frist senest</string>\n    <string name=\"dashclock_show_overdue_tasks\">Vis forfalte gjøremål</string>\n    <string name=\"dashclock_overdue_tasks_show\">Forfalte gjøremål vil bli vist</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Forfalte gjøremål vil bli skjult</string>\n    <string name=\"dashclock_header_shown\">Listenavn med fet skrift</string>\n    <string name=\"dashclock_first_task_shown\">Navn på første gjøremål med fet skrift</string>\n    <string name=\"backup_export_msg\">Eksporter alle notater til %1$s\\?</string>\n    <string name=\"editor\">Tekstredigerer</string>\n    <string name=\"prefs_improved_reliability\">Innstillinger for økt pålitelighet</string>\n    <string name=\"exact_alarms_summary\">Bruker mer batteri for å vise merknader for påminnelser mer pålitelig</string>\n    <string name=\"allow_exact_reminders_summary\">Åpne en side for å tillate dette programmet å sende påminnelser mer pålitelig. Trengs kun siden Android 12 Snow Cone</string>\n    <string name=\"bold\">Fet</string>\n    <string name=\"app_about_license\">Lisensiert GPLv3+, tilgjengelig på https://www.gnu.org/licenses</string>\n    <string name=\"backup_import_msg\">Prøv å importere sikkerhetskopi fra %1$s\\? Dette tømmer nåværende database.</string>\n    <string name=\"sd_card_summary\">Gjøremål vil være de samme i programmet og SD-kortet. Merk at sletting av filer derfor også sletter gjøremålene i programmet!</string>\n    <string name=\"open_channel_settings_description\">Åpne en side for å redigere merknadsinnstillingene for programmet. Disse overskrive alle innstillinger valgt annensteds hen</string>\n    <string name=\"disable_android_hibernation_desc\">Denne funksjonen gjør programmet mindre pålitelig og kan blokkere påminnelsene dine. Åpne dette, se etter noe ala «hvilemodus for program hvis ubrukt» og skru det av</string>\n    <string name=\"msg_enable_notifications\">Gå til innstillinger → merknader for å skru dem på</string>\n    <string name=\"disable_android_hibernation\">Skru av Android-dvalgang</string>\n    <string name=\"unavailable_chose_directory\">Utilgjengelig. Velg en mappe først</string>\n    <string name=\"notifications_enabled\">Merknader synlige</string>\n    <string name=\"notifications_blocked\">Merknader er usynlige, slik at du ikke ser påminnelsene. Trykk her, finn tilgangskategorien, og skru på merknader for dette programmet</string>\n    <string name=\"not_selected_yet\">Ingen valgt enda</string>\n    <string name=\"directory_summary_msg\">Som følge av begrensninger fra Google er filer lagret i %s\\nsom du har tilgang til med filbehandleren. Merk at å avinstallere programmet også sletter disse filene. Bruk sikkerhetskopifunksjonen for å beholde notatene dine</string>\n    <string name=\"notifications_visibility\">Merknadssynlighet</string>\n    <string name=\"msg_hibernation_already_off\">Android-dvalgang er allerede avslått. Du trenger ikke dette</string>\n    <string name=\"password\">Passord</string>\n    <string name=\"canceled_note_locked\">Avbrutt: Notatet er låst</string>\n    <string name=\"select_all\">Velg alt</string>\n    <string name=\"tutorial_online\">Veiledning (nettbasert)</string>\n    <string name=\"welcome_note_title\">Velkommen!</string>\n    <string name=\"welcome_note_row_2\">Åpne dette for å begynne.</string>\n    <string name=\"show_completed_notes\">Vis fullførte notater</string>\n    <string name=\"order_notes_by\">Sorter notater etter:</string>\n    <string name=\"showcase_tutorial_title\">Fortapt?</string>\n    <string name=\"showcase_tutorial_description\">Du finner en nettbasert veiledning i innstillingene</string>\n    <string name=\"app_about_donations\">Du kan donere. Støtt arbeidet ved å finne %1$s i %2$s</string>\n    <string name=\"welcome_note_row_3\">Dette programmet har en detaljert veiledning du finner i</string>\n    <string name=\"next_year\">Neste år</string>\n    <string name=\"unsupported_readonly_file\">Ustøttet skrivebeskyttet fil: %s</string>\n    <string name=\"show_elements_as\">Vis elementer som:</string>\n    <string name=\"version\">Versjon: %s</string>\n    <string name=\"next_month\">Neste måned</string>\n    <string name=\"enable_sync\">Skru på synkroniseing</string>\n    <string name=\"enable_sync_desc\">Skrur på synkroniseringsfunksjoner og lar deg velge en synkroniseringskilde. Sikkerhetskopier blir ikke påvirket</string>\n    <string name=\"changelog_online\">Endringslogg (nettbasert)</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notities</string>\n    <string name=\"title_create\">Nieuwe notitie</string>\n    <string name=\"timemachine\">Tijdmachine</string>\n    <string name=\"time\">Tijd</string>\n    <string name=\"done\">Klaar</string>\n    <string name=\"saved\">Opgeslagen</string>\n    <string name=\"menu_save_and_add\">Opslaan en open nieuwe notitie</string>\n    <string name=\"menu_delete\">Verwijderen</string>\n    <string name=\"menu_deletelist\">Verwijder lijst</string>\n    <string name=\"menu_sync\">Herladen</string>\n    <string name=\"menu_share\">Delen</string>\n    <string name=\"menu_preferences\">Instellingen</string>\n    <string name=\"menu_createlist\">Nieuwe lijst</string>\n    <string name=\"menu_clearcompleted\">Wis voltooide notities</string>\n    <string name=\"menu_setdefaultlist\">Standaard lijst</string>\n    <string name=\"new_default_set\">Nieuw standaardset</string>\n    <string name=\"menu_managelists\">Lijsten beheren</string>\n    <string name=\"add_item\">Toevoegen</string>\n    <string name=\"delete_question\">Verwijderen?</string>\n    <string name=\"delete_items_message\">Ben je zeker dat je deze items wil verwijderen?</string>\n    <string name=\"delete_item_message\">Ben je zeker dat je dit item wilt verwijderen?</string>\n    <string name=\"delete_list_message\">Ben je zeker dat je de lijst wilt verwijderen?</string>\n    <string name=\"editor_due_date_hint\">Einddatum</string>\n    <string name=\"editor_title_hint\">Titel</string>\n    <string name=\"editor_note_hint\">Notitie</string>\n    <string name=\"resolve_edit\">Notitie bewerken</string>\n    <string name=\"default_style\">Standaardstijl</string>\n    <string name=\"show_items_as_tasks\">Toon items als taken</string>\n    <string name=\"show_items_as_notes\">Toon items als notities</string>\n    <string name=\"sort_list_default\">Standaard volgorde</string>\n    <string name=\"sort_list_alphabetical\">Rangschik alfabetisch</string>\n    <string name=\"sort_list_due\">Rangschik volgens vervaldatum</string>\n    <string name=\"sort_list_updated\">Rangschik volgens laatst geüpdatet</string>\n    <string name=\"sort_list_manual\">Rangschik handmatig</string>\n    <string name=\"search_description\">Notities en taken</string>\n    <string name=\"archive\">Archief</string>\n    <string name=\"restore\">Herstellen</string>\n    <string name=\"restore_to\">Herstellen naar</string>\n    <string name=\"search_hint\">Zoeken</string>\n    <string name=\"settings_theme\">Huidig thema</string>\n    <string name=\"settings_theme_dialog\">Gebruik thema</string>\n    <string name=\"settings_lang\">Taal</string>\n    <string name=\"settings_summary_theme_black\">Zwart</string>\n    <string name=\"settings_summary_theme_dark\">Donker</string>\n    <string name=\"settings_summary_theme_light\">Licht</string>\n    <string name=\"settings_summary_theme_classic\">Klassiek</string>\n    <string name=\"settings_cat_appearance\">Opmaak</string>\n    <string name=\"preference_preview_text\">Dit is hoe de tekst eruit zal zien</string>\n    <string name=\"settings_account_title\">Selecteer een account</string>\n    <string name=\"settings_account_summary\">Verander account</string>\n    <string name=\"settings_cat_syncing\">Synchronisatie</string>\n    <string name=\"show_from_all_lists\">Alle lijsten</string>\n    <string name=\"lists\">Lijsten</string>\n    <string name=\"deleted\">Verwijderd</string>\n    <string name=\"repeat\">Herhaal</string>\n    <string name=\"once\">Eenmalig</string>\n    <string name=\"always\">Altijd</string>\n    <string name=\"permission_read_label\">lees notities en lijsten</string>\n    <string name=\"permission_read_desc\">Staat de applicatie toe notities en gerelateerde lijsten te lezen</string>\n    <string name=\"permission_write_label\">notities en lijsten bewerken</string>\n    <string name=\"permission_write_desc\">Staat de applicatie toe om notities en lijsten te maken, bewerken en verwijderen</string>\n    <string name=\"settings_list_dialog\">Selecteer een lijst</string>\n    <string name=\"settings_list\">Lijst</string>\n    <string name=\"drag_to_timetravel\">Sleep om te tijdreizen</string>\n    <string name=\"import_data_question\">Importeer gegevens?</string>\n    <string name=\"import_started\">Gegevens aan het importeren…</string>\n    <string name=\"imported_result\">%1$d notities in %2$d lijsten geïmporteerd</string>\n    <string name=\"import_error\">Er ging iets verkeerd: %s</string>\n    <string name=\"move\">Verplaats</string>\n    <string name=\"move_to\">Verplaats naar</string>\n    <string name=\"moved_x_to_list\">%1$d naar %2$s verplaatst</string>\n    <string name=\"lock_note\">Notitie vergrendelen</string>\n    <string name=\"unlock_note\">Notitie ontgrendelen</string>\n    <string name=\"locked\">Vergrendeld</string>\n    <string name=\"unlocked\">Ontgrendeld</string>\n    <string name=\"password_required\">Wachtwoord vereist</string>\n    <string name=\"select_account\">Selecteer een account</string>\n    <string name=\"password\">Wachtwoord</string>\n    <string name=\"password_set\">Wachtwoord instellen</string>\n    <string name=\"password_cleared\">Wachtwoord verwijderd</string>\n    <string name=\"passwords_dont_match\">Wachtwoorden komen niet overeen</string>\n    <string name=\"password_incorrect\">Ongeldig wachtwoord</string>\n    <string name=\"enter_password\">Wachtwoord invoeren</string>\n    <string name=\"enter_new_password\">Nieuw wachtwoord invoeren</string>\n    <string name=\"confirm_new_password\">Bevestig nieuw wachtwoord</string>\n    <string name=\"apply\">Toepassen</string>\n    <string name=\"clear_password\">Wachtwoord wissen</string>\n    <string name=\"password_info\">Na het instellen van een wachtwoord, zal je een wachtwoord in moeten voeren om vergrendelde notities te bekijken. Het zal alleen het notitieveld beschermen, dus je hebt wel onbeschermd toegang tot de titel en de einddatum. Het versleutelt de notitie niet, dus de notitie is gewoon zichtbaar in Google Mail/Agenda. Als je het wachtwoord vergeet, synchroniseer dan met Google en installeer de app opnieuw.</string>\n    <string name=\"about\">Over</string>\n    <string name=\"sync_failed\">Synchronisatie mislukt</string>\n    <string name=\"sync_login_failed\">Inloggen mislukt, kon niet verbinden met Google Tasks</string>\n    <string name=\"completed\">Voltooid</string>\n    <string name=\"date_header_overdue\">Over tijd</string>\n    <string name=\"date_header_today\">Vandaag</string>\n    <string name=\"date_header_tomorrow\">Morgen</string>\n    <string name=\"date_header_future\">Later</string>\n    <string name=\"date_header_none\">Geen datum</string>\n    <string name=\"date_header_completed\">Voltooid</string>\n    <string name=\"next_5_days\">Volgende 5 dagen</string>\n    <string name=\"next_n_days\">Volgende %d dagen</string>\n    <string name=\"this_week\">Deze week</string>\n    <string name=\"please_select_note\">Selecteer of maak eerst een notitie</string>\n    <string name=\"please_create_note\">Maak eerst een notitie</string>\n    <string name=\"hide_checkbox\">Vinkje verbergen</string>\n    <string name=\"hide_checkbox_summary_on\">Vinkje wordt verborgen</string>\n    <string name=\"hide_checkbox_summary_off\">Vinkje wordt getoond</string>\n    <string name=\"hide_date\">Einddatum verbergen</string>\n    <string name=\"item_max_height\">Maximum hoogte van notities/taken</string>\n    <string name=\"long_date_format\">Lange datumnotatie</string>\n    <string name=\"short_date_format\">Korte datumnotatie</string>\n    <string name=\"select_date\">Selecteer datum</string>\n    <string name=\"localedefault\">Standaard</string>\n    <string name=\"hide_header\">Hele widget-kop verbergen</string>\n    <string name=\"transparency\">Transparantie</string>\n    <string name=\"notes_shortcut\">Notities snelkoppeling</string>\n    <string name=\"shortcut_help1\">Indien ingeschakeld maakt snelkoppeling een nieuwe notitie in de gekozen lijst en opent de bewerker. Indien uitgeschakeld opent de snelkoppeling de gekozen lijst.</string>\n    <string name=\"loading_widget\">Laden widget…</string>\n    <string name=\"default_list\">Standaard lijst</string>\n    <string name=\"please_type_before_reminder\">Typ een tekst voordat je een herinnering plaatst</string>\n    <string name=\"notecopied_one\">1 notitie gekopieerd</string>\n    <string name=\"notecopied_other\">%d notities gekopieerd</string>\n    <string name=\"notedeleted_one\">1 notitie verwijderd</string>\n    <string name=\"notedeleted_other\">%d notities verwijderd</string>\n    <string name=\"selected_one\">1 notitie geselecteerd</string>\n    <string name=\"selected_other\">%d notities geselecteerd</string>\n    <string name=\"background_sync\">Achtergrondsynchronisatie</string>\n    <string name=\"background_sync_info\">Synchoniseert een keer per uur</string>\n    <string name=\"sync_on_change\">Synchroniseer bij wijzigingen</string>\n    <string name=\"sync_on_change_info\">Synchroniseer kort nadat een notitie is gewijzigd</string>\n    <string name=\"sync_on_start\">Synchroniseer bij starten app</string>\n    <string name=\"sync_on_start_info\">Synchroniseert bij nieuwe start, niet bij hervatten app</string>\n    <string name=\"snooze\">Dutten</string>\n    <string name=\"silent\">Stil</string>\n    <string name=\"sound\">Geluid</string>\n    <string name=\"vibrate\">Vibratie</string>\n    <string name=\"reminders\">Herinneringen</string>\n    <string name=\"standard\">Standaard</string>\n    <string name=\"notification_prio_high\">Hoog: Bovenaan, uitgevouwen</string>\n    <string name=\"notification_prio_low\">Laag: Verborgen, geminimaliseerd</string>\n    <string name=\"priority\">Prioriteit</string>\n    <string name=\"notifications\">Meldingen</string>\n    <string name=\"tasks\">Taken</string>\n    <string name=\"all_tasks\">Alle taken</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Beperk tot taken uit een specifieke lijst</string>\n    <string name=\"dashclock_show_overdue_tasks\">Toon verlopen taken</string>\n    <string name=\"dashclock_overdue_tasks_show\">Verlopen taken worden getoond</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Verlopen taken worden verborgen</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense Notes Instellingen</string>\n    <string name=\"dashclock_pref_header_general\">Algemeen</string>\n    <string name=\"dashclock_due_upper_limit_title\">Beperk tot taken die uiterlijk verlopen</string>\n    <string name=\"dashclock_today\">Vandaag</string>\n    <string name=\"dashclock_tomorrow\">Morgen</string>\n    <string name=\"dashclock_next7\">Volgende 7 dagen</string>\n    <string name=\"dashclock_anytime\">Willekeurig</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Toont er zoveel mogelijk</string>\n    <string name=\"dashclock_show_only_the_next_task\">Toon alleen de volgende taak</string>\n    <string name=\"dashclock_header_shown\">Lijstnamen worden vet getoond.  Als \\\"Alle Lijsten\\\" is geselecteerd,  \\\"Taken\\\" wordt getoond</string>\n    <string name=\"dashclock_first_task_shown\">Titel van de eerste taak wordt vet getoond</string>\n    <string name=\"dashclock_display_header\">Kop weergave</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Vet</string>\n    <string name=\"italic\">Cursief</string>\n    <string name=\"normal\">Normaal</string>\n    <string name=\"title_style\">Titel stijl</string>\n    <string name=\"title_font\">Titel lettertype</string>\n    <string name=\"body_font\">Tekst lettertype</string>\n    <string name=\"clickable_links\">Klikbare links</string>\n    <string name=\"small\">Klein</string>\n    <string name=\"medium\">Middelgrrot</string>\n    <string name=\"large\">Groot</string>\n    <string name=\"text_size\">Tekstgrootte</string>\n    <string name=\"text\">Tekst</string>\n    <string name=\"add_a_reminder\">Voeg een herinnering toe</string>\n    <string name=\"backup\">Back-up</string>\n    <string name=\"backup_import\">Back-up importeren</string>\n    <string name=\"backup_export\">Back-up exporteren</string>\n    <string name=\"backup_import_msg\">Probeer back-up te importeren uit %1$s? De huidige database wordt hiermee gewist.</string>\n    <string name=\"backup_export_msg\">Alle notities exporteren naar %1$s?</string>\n    <string name=\"backup_import_success\">Back-up succesvol geëmporteerd</string>\n    <string name=\"backup_file_not_found\">Get back-upbestand kon niet worden gevonden</string>\n    <string name=\"backup_import_failed\">Lezen van het back-upbestand is mislukt</string>\n    <string name=\"backup_export_success\">Back-up successvol geëxporteerd</string>\n    <string name=\"backup_export_failed\">Kan niet schrijven naar het back-upbestand</string>\n    <string name=\"sd_card\">SD-kaart</string>\n    <string name=\"sd_card_summary\">De taken in de app zijn een afspiegeling van de SD-kaart. Wijzigingen in beide worden direct doorgevoerd. Als je de bestanden verwijderd, verdwijnen ook de taken in de app!</string>\n    <string name=\"directory\">Directory</string>\n    <string name=\"cannot_write_to_directory\">Kan niet schrijven naar directory</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notatki</string>\n    <string name=\"title_create\">Nowa notatka</string>\n    <string name=\"timemachine\">Kapsuła czasu</string>\n    <string name=\"time\">Czas</string>\n    <string name=\"done\">Gotowe</string>\n    <string name=\"saved\">Zapisane</string>\n    <string name=\"menu_save_and_add\">Nowa notatka</string>\n    <string name=\"menu_delete\">Usuń</string>\n    <string name=\"menu_deletelist\">Usuń listę</string>\n    <string name=\"menu_sync\">Odśwież</string>\n    <string name=\"menu_share\">Udostępnij</string>\n    <string name=\"menu_preferences\">Ustawienia</string>\n    <string name=\"menu_createlist\">Utwórz listę</string>\n    <string name=\"menu_clearcompleted\">Wyczyść zakończone</string>\n    <string name=\"menu_setdefaultlist\">Ustaw jako domyślną listę</string>\n    <string name=\"new_default_set\">Nowe ustawienie domyślne</string>\n    <string name=\"menu_managelists\">Zarządzaj listą</string>\n    <string name=\"add_item\">Dodaj</string>\n    <string name=\"delete_question\">Usuwanie?</string>\n    <string name=\"delete_items_message\">Usunąć te elementy\\?</string>\n    <string name=\"delete_item_message\">Usunąć ten element\\?</string>\n    <string name=\"delete_list_message\">Usunąć tę listę\\?</string>\n    <string name=\"editor_due_date_hint\">Termin</string>\n    <string name=\"editor_title_hint\">Tytuł</string>\n    <string name=\"editor_note_hint\">Notatka</string>\n    <string name=\"resolve_edit\">Edytuj notatkę</string>\n    <string name=\"default_style\">Wyświetlanie domyślne </string>\n    <string name=\"show_items_as_tasks\">zadania, które można sprawdzić</string>\n    <string name=\"show_items_as_notes\">proste notatki</string>\n    <string name=\"sort_list_default\">Domyślna kolejność sortowania</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_due\">Według terminu</string>\n    <string name=\"sort_list_updated\">Ostatnio zaktualizowane</string>\n    <string name=\"sort_list_manual\">Instrukcja</string>\n    <string name=\"search_description\">Notatki i zadania </string>\n    <string name=\"archive\">Archiwum</string>\n    <string name=\"restore\">Przywróć</string>\n    <string name=\"restore_to\">Przywróć do</string>\n    <string name=\"search_hint\">Szukaj</string>\n    <string name=\"settings_theme\">Motyw</string>\n    <string name=\"settings_theme_dialog\">Użyj motywu</string>\n    <string name=\"settings_lang\">Język</string>\n    <string name=\"settings_summary_theme_black\">Czarny</string>\n    <string name=\"settings_summary_theme_dark\">Ciemny</string>\n    <string name=\"settings_summary_theme_light\">Jasny</string>\n    <string name=\"settings_summary_theme_classic\">Klasyczny</string>\n    <string name=\"settings_cat_appearance\">Wygląd</string>\n    <string name=\"preference_preview_text\">Tak będzie wyglądał tekst w edytorze</string>\n    <string name=\"settings_account_title\">Wybierz konto</string>\n    <string name=\"settings_account_summary\">Kliknij, aby zmienić konto</string>\n    <string name=\"settings_cat_syncing\">Synchronizacja</string>\n    <string name=\"show_from_all_lists\">Wszystkie listy</string>\n    <string name=\"lists\">Listy</string>\n    <string name=\"deleted\">Usunięto</string>\n    <string name=\"repeat\">Powtarzanie</string>\n    <string name=\"once\">Jeden raz</string>\n    <string name=\"always\">Zawsze</string>\n    <string name=\"permission_read_label\">czytaj notatki i listy</string>\n    <string name=\"permission_read_desc\">Umożliwia aplikacji odczytywanie notatek i powiązanych list w aplikacji No Nonsense Notes</string>\n    <string name=\"permission_write_label\">zmodyfikuj notatki i listy</string>\n    <string name=\"permission_write_desc\">Umożliwia aplikacji wstawianie, aktualizowanie i usuwanie notatek i list w No Nonsense Notes</string>\n    <string name=\"settings_list_dialog\">Wybierz listę</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"drag_to_timetravel\">Poprzednie wersje </string>\n    <string name=\"import_data_question\">Skopiować dane?</string>\n    <string name=\"import_started\">Importowanie danych…</string>\n    <string name=\"imported_result\">Zaimportowano %1$d notatek do %2$d list</string>\n    <string name=\"import_error\">Coś poszło nie tak: %s</string>\n    <string name=\"move\">Przenieś</string>\n    <string name=\"move_to\">Przenieś do</string>\n    <string name=\"lock_note\">Zablokuj notatkę</string>\n    <string name=\"unlock_note\">Odblokuj notatkę</string>\n    <string name=\"locked\">Zablokowana</string>\n    <string name=\"unlocked\">Odblokowana</string>\n    <string name=\"password_required\">Wymagane hasło</string>\n    <string name=\"select_account\">Wybierz konto</string>\n    <string name=\"password\">Hasło</string>\n    <string name=\"password_set\">Hasło ustawione</string>\n    <string name=\"password_cleared\">Hasło usunięte</string>\n    <string name=\"passwords_dont_match\">Hasła się nie zgadzają </string>\n    <string name=\"password_incorrect\">Hasło niepoprawne </string>\n    <string name=\"enter_password\">Wprowadź hasło</string>\n    <string name=\"enter_new_password\">Wprowadź nowe hasło</string>\n    <string name=\"confirm_new_password\">Potwierdź nowe hasło</string>\n    <string name=\"apply\">Zastosuj</string>\n    <string name=\"clear_password\">Usuń hasło</string>\n    <string name=\"password_info\">Każda notatka może zostać zablokowana, co ogranicza możliwość jej przeglądania i modyfikowania. Pozostaje ona w pełni dostępna na innych urządzeniach.</string>\n    <string name=\"about\">O programie</string>\n    <string name=\"sync_failed\">Synchronizacja nieudana</string>\n    <string name=\"sync_login_failed\">Nie można zalogować się do Google Tasks</string>\n    <string name=\"completed\">Zakończono</string>\n    <string name=\"date_header_overdue\">Po terminie</string>\n    <string name=\"date_header_today\">Dzisiaj</string>\n    <string name=\"date_header_tomorrow\">Jutro</string>\n    <string name=\"date_header_future\">Później</string>\n    <string name=\"date_header_none\">Bez daty</string>\n    <string name=\"date_header_completed\">Zakończone</string>\n    <string name=\"next_5_days\">Następne 5 dni </string>\n    <string name=\"next_n_days\">Następne %d dni</string>\n    <string name=\"this_week\">Ten tydzień</string>\n    <string name=\"please_select_note\">Proszę zaznaczyć lub stworzyć notatkę</string>\n    <string name=\"please_create_note\">Proszę stworzyć notatkę</string>\n    <string name=\"hide_checkbox\">Ukryj pole wyboru</string>\n    <string name=\"hide_checkbox_summary_on\">Pole wyboru ukryte</string>\n    <string name=\"hide_checkbox_summary_off\">Pole wyboru widoczne</string>\n    <string name=\"hide_date\">Ukryj termin</string>\n    <string name=\"item_max_height\">Maksymalna długość nagłówka</string>\n    <string name=\"long_date_format\">Długi format daty dla panelu</string>\n    <string name=\"short_date_format\">Krótki format daty dla listy</string>\n    <string name=\"select_date\">Ustaw datę</string>\n    <string name=\"localedefault\">Domyślnie ustawienia regionalne</string>\n    <string name=\"hide_header\">Ukryj nagłówek widżetu</string>\n    <string name=\"transparency\">Przeźroczystość</string>\n    <string name=\"notes_shortcut\">Skrót notatki</string>\n    <string name=\"shortcut_help1\">Sprawia, że skrót otwiera nową notatkę na wybranej liście z otwartym edytorem tekstu.</string>\n    <string name=\"loading_widget\">Ładowanie widżetu…</string>\n    <string name=\"default_list\">Domyślna lista</string>\n    <string name=\"please_type_before_reminder\">Wpisz tekst przed dodaniem przypomnienia</string>\n    <string name=\"notecopied_one\">Skopiowano jedną notatkę</string>\n    <string name=\"notecopied_other\">Skopiowano %d notatki</string>\n    <string name=\"notedeleted_one\">Usunięto jedną notatkę</string>\n    <string name=\"notedeleted_other\">Usunięto %d notatki</string>\n    <string name=\"selected_one\">Zaznaczono jedną notatkę</string>\n    <string name=\"selected_other\">Zaznaczono %d notatki</string>\n    <string name=\"background_sync\">Synchronizuj w tle</string>\n    <string name=\"background_sync_info\">Synchronizacja raz na godzinę</string>\n    <string name=\"sync_on_change\">Synchronizuj przy zmianach</string>\n    <string name=\"sync_on_change_info\">Synchronizacja zaraz po zmianie notatki</string>\n    <string name=\"sync_on_start\">Synchronizuj przy starcie aplikacji </string>\n    <string name=\"sync_on_start_info\">Ograniczenie synchronizacji do raz na 5 minut</string>\n    <string name=\"snooze\">Drzemka</string>\n    <string name=\"silent\">Cichy</string>\n    <string name=\"sound\">Dźwięk </string>\n    <string name=\"vibrate\">Wibracje</string>\n    <string name=\"reminders\">Przypomnienia </string>\n    <string name=\"standard\">Standardowy</string>\n    <string name=\"notification_prio_high\">Wysoki: na górze, rozszerzone </string>\n    <string name=\"notification_prio_low\">Niski: ukryte, zminimalizowane</string>\n    <string name=\"priority\">Priorytet </string>\n    <string name=\"notifications\">Powiadomienia</string>\n    <string name=\"tasks\">Zadania </string>\n    <string name=\"all_tasks\">Wszystkie zadania</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Ogranicz zadania do określonej listy</string>\n    <string name=\"dashclock_show_overdue_tasks\">Wyświetl zaległe zadania</string>\n    <string name=\"dashclock_overdue_tasks_show\">Wyświetlanie zaległych zadań</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Zaległe zadania nie będą wyświetlane</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Ustawienia</string>\n    <string name=\"dashclock_pref_header_general\">Ogólny</string>\n    <string name=\"dashclock_due_upper_limit_title\">Ogranicz do zadań występujących</string>\n    <string name=\"dashclock_today\">Dziś</string>\n    <string name=\"dashclock_tomorrow\">Jutro</string>\n    <string name=\"dashclock_next7\">W następnych 7 dniach</string>\n    <string name=\"dashclock_anytime\">Kiedykolwiek</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Pokaż jak najwięcej</string>\n    <string name=\"dashclock_show_only_the_next_task\">Wyświetlaj tylko nastepne zadanie</string>\n    <string name=\"dashclock_header_shown\">Pogrubiona nazwa listy</string>\n    <string name=\"dashclock_first_task_shown\">Pogrubiony tytuł pierwszego zadania</string>\n    <string name=\"dashclock_display_header\">Wyświetlaj nagłówek</string>\n    <string name=\"editor\">Edytor</string>\n    <string name=\"bold\">Pogrubienie</string>\n    <string name=\"italic\">Kursywa</string>\n    <string name=\"normal\">Normalny</string>\n    <string name=\"title_style\">Styl Tytułu</string>\n    <string name=\"title_font\">Czcionka Tytułu</string>\n    <string name=\"body_font\">Czcionka Tekstu</string>\n    <string name=\"clickable_links\">Klikalne linki</string>\n    <string name=\"small\">Mały</string>\n    <string name=\"medium\">Średni</string>\n    <string name=\"large\">Duży</string>\n    <string name=\"text_size\">Rozmiar tekstu</string>\n    <string name=\"text\">Tekst</string>\n    <string name=\"add_a_reminder\">Dodaj przypomnienie</string>\n    <string name=\"backup\">Kopia zapasowa</string>\n    <string name=\"backup_import\">Importuj kopię zapasową</string>\n    <string name=\"backup_export\">Eksportuj kopię zapasową</string>\n    <string name=\"backup_import_msg\">Spróbować zaimportować kopię zapasową z %1$s? Spowoduje to wyczyszczenie bierzącej bazy danych.</string>\n    <string name=\"backup_export_msg\">Eksportować wszystkie notatki do %1$s?</string>\n    <string name=\"backup_import_success\">Kopia zapasowa zaimportowana</string>\n    <string name=\"backup_file_not_found\">Nie można znaleźć pliku kopii zapasowej</string>\n    <string name=\"backup_import_failed\">Nie można odczytać pliku kopi zapasowej</string>\n    <string name=\"backup_export_success\">Kopia zapasowa wyeksportowana</string>\n    <string name=\"backup_export_failed\">Nie można zapisać pliku kopii zapasowej</string>\n    <string name=\"sd_card\">Karta SD</string>\n    <string name=\"sd_card_sync\">Synchronizacja karty SD</string>\n    <string name=\"sd_card_summary\">Zadania są takie same w aplikacji i na karcie SD. Usunięcie plików powoduje zatem usunięcie zadań w aplikacji!</string>\n    <string name=\"directory\">Katalog</string>\n    <string name=\"cannot_write_to_directory\">Nie można zapisać do katalogu</string>\n    <string name=\"delete_completed_tasks_question\">Usunąć wszystkie ukończone zadania\\?</string>\n    <string name=\"battery_optimizations_inactive\">Optymalizacja baterii wyłączona</string>\n    <string name=\"feature_is_WIP\">Ta funkcja to W.I.P.</string>\n    <string name=\"use_exact_alarms\">Używaj dokładnych alarmów</string>\n    <string name=\"file_picker_not_available\">Selektor plików jest niedostępny</string>\n    <string name=\"disable_battery_optimizations\">Wyłącz optymalizacje baterii</string>\n    <string name=\"navigation_drawer_close\">Zamknij szufladę nawigacji</string>\n    <string name=\"battery_optimizations_active\">Optymalizacje baterii na</string>\n    <string name=\"navigation_drawer_open\">Otwórz szufladę nawigacji</string>\n    <string name=\"no_sync_method_chosen\">Nie wybrano metody synchronizacji</string>\n    <string name=\"choose_backup_folder\">Wybierz folder kopii zapasowej</string>\n    <string name=\"allow_exact_reminders\">Pozwól na dokładne przypomnienia</string>\n    <string name=\"notification_channel_name\">Przypomnienia dla notatek</string>\n    <string name=\"permission_denied\">Nie można kontynuować: odmówiono zezwolenia</string>\n    <string name=\"notification_channel_description\">Wyświetla treść notatki w momencie wybranego przypomnienia</string>\n    <string name=\"exact_alarms_summary\">Zużywa więcej baterii, aby wyświetlać przypomnienia o powiadomieniach w bardziej niezawodny sposób</string>\n    <string name=\"prefs_improved_reliability\">Preferencje w zakresie poprawy niezawodności</string>\n    <string name=\"changelog_online\">Lista zmian (online)</string>\n    <string name=\"enable_sync_desc\">Włącza funkcje synchronizacji i pozwala wybrać źródło synchronizacji. Nie ma to wpływu na kopie zapasowe</string>\n    <string name=\"order_notes_by\">Uszereguj notatki przez:</string>\n    <string name=\"canceled_note_locked\">Anulowano: notatka jest zablokowana</string>\n    <string name=\"allow_exact_reminders_summary\">Otwórz stronę, aby umożliwić aplikacji bardziej niezawodne wysyłanie przypomnień. Wymagane od wersji Androida 12 Snow Cone</string>\n    <string name=\"unavailable_chose_directory\">Niedostępne. Najpierw wybierz katalog</string>\n    <string name=\"app_about_dev_team\">Stworzony przez Jonasa Kalderstama i utrzymywany przez Campello Manuela</string>\n    <string name=\"app_about_license\">Licencja GPLv3+, https://www.gnu.org/licenses</string>\n    <string name=\"app_about_bugreports\">Zgłaszaj błędy i problemy na stronie https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"overwritten_in_newer_systems\">Można cofnąć niektóre ustawienia kanału powiadomień</string>\n    <string name=\"open_channel_settings_description\">Otwórz stronę, aby edytować ustawienia powiadomień dla tej aplikacji. Zastępują one wszelkie ustawienia wybrane w innym miejscu</string>\n    <string name=\"disable_android_hibernation_desc\">Jest to funkcja, która sprawia, że aplikacja jest mniej niezawodna. Może blokować przypomnienia. Otwórz to, poszukaj czegoś takiego jak \\\"wstrzymaj aktywność aplikacji, jeśli nie jest używana\\\" i wyłącz ją</string>\n    <string name=\"msg_hibernation_already_off\">Hibernacja Androida jest już WYŁĄCZONA, nie potrzebujesz tego</string>\n    <string name=\"notification_channel_settings\">Ustawienia kanału powiadomień</string>\n    <string name=\"notifications_blocked\">Powiadomienia nie są wyświetlane, więc nie zobaczysz przypomnień. Dotknij tutaj, znajdź kategorię uprawnień i włącz powiadomienia dla tej aplikacji</string>\n    <string name=\"app_about_donations\">Przyjmujemy darowizny. Aby wesprzeć naszą pracę, poszukaj %1$s w %2$s</string>\n    <string name=\"app_about_git_repo\">Ta aplikacja to oprogramowanie typu copyleft libre, dostępne pod adresem https://github.com/spacecowboy/NotePad</string>\n    <string name=\"notifications_visibility\">Wyświetlanie powiadomień</string>\n    <string name=\"not_selected_yet\">Jeszcze nie wybrano</string>\n    <string name=\"notifications_enabled\">Powiadomienia są wyświetlane</string>\n    <string name=\"enable_sync\">Włącz synchronizację</string>\n    <string name=\"disable_android_hibernation\">Wyłącz hibernację systemu Android</string>\n    <string name=\"app_about_translations\">Pomóż przetłumaczyć aplikację na stronie https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"for_older_devices\">Dla starszych urządzeń z systemem Android</string>\n    <string name=\"msg_enable_notifications\">Przejdź do ustawień -&gt; powiadomienia, aby włączyć powiadomienia</string>\n    <string name=\"libraries_used\">Biblioteki</string>\n    <string name=\"version\">Wersja: %s</string>\n    <string name=\"next_month\">Następny miesiąc</string>\n    <string name=\"welcome_note_title\">Witaj!</string>\n    <string name=\"unsupported_readonly_file\">Nieobsługiwany plik tylko do odczytu: %s</string>\n    <string name=\"moved_x_to_list\">Przeniesiono %1$d do %2$s</string>\n    <string name=\"account\">Konto</string>\n    <string name=\"next_year\">Następny rok</string>\n    <string name=\"select_all\">Wybierz wszystko</string>\n    <string name=\"showcase_tutorial_title\">Czujesz się zagubiona/y?</string>\n    <string name=\"welcome_note_row_2\">Otwórz, aby rozpocząć.</string>\n    <string name=\"showcase_tutorial_description\">Mamy samouczek online. Można go znaleźć w ustawieniach</string>\n    <string name=\"tutorial_online\">Samouczek (online)</string>\n    <string name=\"welcome_note_row_3\">Ta aplikacja zawiera szczegółowy samouczek. Można go znaleźć pod adresem</string>\n    <string name=\"show_elements_as\">Pokaż pozycje jako:</string>\n    <string name=\"show_completed_notes\">Pokaż uzupełnione notatki</string>\n    <string name=\"directory_summary_msg\">Pliki są zapisywane w %s\n\\ndo którego można uzyskać dostęp za pomocą aplikacji do zarządzania plikami. Odinstalowanie aplikacji spowoduje również usunięcie tych plików: aby zachować notatki, wykonaj kopię zapasową</string>\n    <string name=\"bigger_titles\">Duże tytuły</string>\n    <string name=\"bigger_titles_summary\">Tytuł to pierwsza linia</string>\n    <string name=\"sorting\">Sortowanie</string>\n    <string name=\"undo\">Cofnij</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name_short\">Notas</string>\n    <string name=\"title_create\">Nova nota</string>\n    <string name=\"timemachine\">Linha do tempo</string>\n    <string name=\"time\">Horário</string>\n    <string name=\"done\">Pronto</string>\n    <string name=\"saved\">Gravado</string>\n    <string name=\"menu_save_and_add\">Gravar e abrir nova nota</string>\n    <string name=\"menu_delete\">Eliminar</string>\n    <string name=\"menu_deletelist\">Apagar lista</string>\n    <string name=\"menu_sync\">Sincronizar</string>\n    <string name=\"menu_createlist\">Criar lista</string>\n    <string name=\"delete_items_message\">Apagar estes itens?</string>\n    <string name=\"delete_item_message\">Apagar este item?</string>\n    <string name=\"delete_question\">Apagar?</string>\n    <string name=\"delete_list_message\">Apagar esta lista?</string>\n    <string name=\"delete_completed_tasks_question\">Apagar todas tarefas completadas?</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_due\">Data limite</string>\n    <string name=\"sort_list_updated\">Última atualização</string>\n    <string name=\"sort_list_manual\">Manual</string>\n    <string name=\"restore\">Restaurar</string>\n    <string name=\"search_description\">Notas e tarefas</string>\n    <string name=\"archive\">Arquivar</string>\n    <string name=\"restore_to\">Restaurar para</string>\n    <string name=\"search_hint\">Procurar</string>\n    <string name=\"settings_theme\">Tema atual</string>\n    <string name=\"settings_theme_dialog\">Usar tema</string>\n    <string name=\"settings_lang\">Idioma</string>\n    <string name=\"settings_summary_theme_black\">Preto</string>\n    <string name=\"settings_summary_theme_dark\">Escuro</string>\n    <string name=\"settings_summary_theme_light\">Claro</string>\n    <string name=\"settings_summary_theme_classic\">Clássico</string>\n    <string name=\"settings_account_summary\">Clique para alterar a conta</string>\n    <string name=\"show_from_all_lists\">Todas as listas</string>\n    <string name=\"lists\">Listas</string>\n    <string name=\"deleted\">Apagado</string>\n    <string name=\"repeat\">Repetir</string>\n    <string name=\"once\">Uma vez</string>\n    <string name=\"permission_write_label\">modificar as notas e as listas</string>\n    <string name=\"settings_list_dialog\">Selecione uma lista</string>\n    <string name=\"notification_channel_name\">Lembretes para notas</string>\n    <string name=\"import_error\">Algo deu errado: %s</string>\n    <string name=\"notification_channel_description\">Mostra o conteúdo da nota no momento escolhido no lembrete</string>\n    <string name=\"feature_is_WIP\">Estamos trabalhando neste recurso.</string>\n    <string name=\"permission_denied\">Não foi possível prosseguir: você negou a permissão</string>\n    <string name=\"choose_backup_folder\">Escolha uma pasta para backup</string>\n    <string name=\"disable_battery_optimizations\">Desativar otimização da pilha</string>\n    <string name=\"for_older_devices\">Para dispositivos Android mais antigos</string>\n    <string name=\"libraries_used\">Bibliotecas</string>\n    <string name=\"app_about_git_repo\">Esta app é um software de código aberto, disponível em https://github.com/spacecowboy/NotePad</string>\n    <string name=\"app_about_translations\">Ajude a traduzir a app em https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"notifications_enabled\">Notificações estão visíveis</string>\n    <string name=\"unavailable_chose_directory\">Indisponível. Por favor, escolha uma pasta primeiro</string>\n    <string name=\"not_selected_yet\">Ainda não selecionado</string>\n    <string name=\"order_notes_by\">Ordernar notas por:</string>\n    <string name=\"show_elements_as\">Mostrar itens como:</string>\n    <string name=\"version\">Versão: %s</string>\n    <string name=\"next_month\">Próximo mês</string>\n    <string name=\"next_year\">Próximo ano</string>\n    <string name=\"select_all\">Selecionar tudo</string>\n    <string name=\"welcome_note_title\">Bem-vindo(a)!</string>\n    <string name=\"welcome_note_row_2\">Para começar, abra isto.</string>\n    <string name=\"show_completed_notes\">Mostrar notas completas</string>\n    <string name=\"move\">Mover</string>\n    <string name=\"account\">Conta</string>\n    <string name=\"moved_x_to_list\">Movido %1$d para %2$s</string>\n    <string name=\"lock_note\">Bloquear nota</string>\n    <string name=\"unlock_note\">Desbloquear nota</string>\n    <string name=\"password\">Palavra-passe</string>\n    <string name=\"password_set\">Definir palavra-passe</string>\n    <string name=\"password_cleared\">Palavra-passe apagada</string>\n    <string name=\"passwords_dont_match\">As palavras-passe não combinam</string>\n    <string name=\"password_incorrect\">Palavra-passe errada</string>\n    <string name=\"enter_password\">Digite a palavra-passe</string>\n    <string name=\"enter_new_password\">Digite a nova palavra-passe</string>\n    <string name=\"confirm_new_password\">Confirme a nova palavra-passe</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"clear_password\">Apagar palavra-passe</string>\n    <string name=\"about\">Sobre</string>\n    <string name=\"sync_failed\">Sincronização falhou</string>\n    <string name=\"completed\">Concluído</string>\n    <string name=\"date_header_overdue\">Atrasado</string>\n    <string name=\"date_header_none\">Sem data</string>\n    <string name=\"date_header_completed\">Completado</string>\n    <string name=\"date_header_future\">Mais tarde</string>\n    <string name=\"next_5_days\">Próximos cinco dias</string>\n    <string name=\"next_n_days\">Próximos %d dias</string>\n    <string name=\"this_week\">Esta semana</string>\n    <string name=\"please_select_note\">Por favor, selecione ou crie uma nota</string>\n    <string name=\"please_create_note\">Por favor, crie uma nota</string>\n    <string name=\"hide_checkbox\">Esconder checkbox</string>\n    <string name=\"hide_date\">Esconder o prazo</string>\n    <string name=\"item_max_height\">Altura máxima das notas/tarefas</string>\n    <string name=\"localedefault\">Localização padrão</string>\n    <string name=\"hide_header\">Esconder todo o cabeçalho do widget</string>\n    <string name=\"loading_widget\">A carregar o widget…</string>\n    <string name=\"default_list\">Lista padrão</string>\n    <string name=\"notecopied_other\">%d notas copiadas</string>\n    <string name=\"notedeleted_other\">%d notas apagadas</string>\n    <string name=\"selected_other\">%d notas selecionadas</string>\n    <string name=\"background_sync\">Sincronização em segundo plano</string>\n    <string name=\"snooze\">Adiar o lembrete</string>\n    <string name=\"silent\">Silêncio</string>\n    <string name=\"sound\">Som</string>\n    <string name=\"reminders\">Lembretes</string>\n    <string name=\"standard\">Padrão</string>\n    <string name=\"priority\">Prioridade</string>\n    <string name=\"notifications\">Notificações</string>\n    <string name=\"tasks\">Tarefas</string>\n    <string name=\"all_tasks\">Todas as tarefas</string>\n    <string name=\"dashclock_show_overdue_tasks\">Mostrar tarefas atrasadas</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Configurações</string>\n    <string name=\"dashclock_pref_header_general\">Geral</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limitar a tarefas que vencem mais antes</string>\n    <string name=\"dashclock_today\">Hoje</string>\n    <string name=\"dashclock_tomorrow\">Amanhã</string>\n    <string name=\"dashclock_anytime\">A qualquer hora</string>\n    <string name=\"bold\">Negrito</string>\n    <string name=\"italic\">Itálico</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"text\">Texto</string>\n    <string name=\"backup_import\">Importar backup</string>\n    <string name=\"backup_export_msg\">Exportar todas as notas para %1$s?</string>\n    <string name=\"backup_import_success\">Backup importado</string>\n    <string name=\"backup_file_not_found\">Não foi possível encontrar o ficheiro do backup</string>\n    <string name=\"backup_import_failed\">Não foi possível ler o ficheiro do backup</string>\n    <string name=\"backup_export_success\">Backup exportado</string>\n    <string name=\"sd_card\">Cartão SD</string>\n    <string name=\"directory\">Pasta</string>\n    <string name=\"cannot_write_to_directory\">Não é possível escrever na pasta</string>\n    <string name=\"bigger_titles\">Títulos maiores</string>\n    <string name=\"bigger_titles_summary\">O título é a primeira linha</string>\n    <string name=\"menu_share\">Partilhar</string>\n    <string name=\"menu_preferences\">Configurações</string>\n    <string name=\"menu_clearcompleted\">Apagar concluídas</string>\n    <string name=\"menu_setdefaultlist\">Definir como lista padrão</string>\n    <string name=\"new_default_set\">Novo padrão definido</string>\n    <string name=\"editor_title_hint\">Título</string>\n    <string name=\"menu_managelists\">Gerir listas</string>\n    <string name=\"add_item\">Adicionar item</string>\n    <string name=\"editor_due_date_hint\">Data limite</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Editar nota</string>\n    <string name=\"default_style\">Estilo padrão</string>\n    <string name=\"settings_cat_appearance\">Aparência</string>\n    <string name=\"preference_preview_text\">É assim que o texto irá aparecer</string>\n    <string name=\"move_to\">Mover para</string>\n    <string name=\"settings_account_title\">Selecione uma conta</string>\n    <string name=\"always\">Sempre</string>\n    <string name=\"permission_read_label\">ler as notas e as listas</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"navigation_drawer_open\">Abrir barra de navegação</string>\n    <string name=\"navigation_drawer_close\">Fechar barra de navegação</string>\n    <string name=\"import_data_question\">Importar dados?</string>\n    <string name=\"no_sync_method_chosen\">Não escolheu um método de sincronização</string>\n    <string name=\"enable_sync\">Ativar sincronização</string>\n    <string name=\"showcase_tutorial_description\">Temos um tutorial online. Encontre-o nas configurações</string>\n    <string name=\"locked\">Bloqueada</string>\n    <string name=\"unlocked\">Desbloqueada</string>\n    <string name=\"password_required\">Palavra-passe necessária</string>\n    <string name=\"select_account\">Selecione uma conta</string>\n    <string name=\"date_header_today\">Hoje</string>\n    <string name=\"date_header_tomorrow\">Amanhã</string>\n    <string name=\"transparency\">Transparência</string>\n    <string name=\"notes_shortcut\">Atalho para notas</string>\n    <string name=\"background_sync_info\">Sincroniza de hora em hora</string>\n    <string name=\"sync_on_change\">Sincronizar após modificações</string>\n    <string name=\"sync_on_change_info\">Sincronizar imediatamente depois que a nota foi modificada</string>\n    <string name=\"sync_on_start\">Sincronização na inicialização do app</string>\n    <string name=\"vibrate\">Vibrar</string>\n    <string name=\"notification_prio_high\">Alta: No topo, expandida</string>\n    <string name=\"notification_prio_low\">Baixa: Escondida, minimizada</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restringir tarefas a uma lista específica</string>\n    <string name=\"dashclock_next7\">Próximos 7 dias</string>\n    <string name=\"dashclock_show_only_the_next_task\">Mostrar somente a próxima tarefa</string>\n    <string name=\"dashclock_display_header\">Mostrar cabeçalho</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"title_style\">Estilo de título</string>\n    <string name=\"title_font\">Fonte do título</string>\n    <string name=\"clickable_links\">Ligações clicáveis</string>\n    <string name=\"small\">Pequeno</string>\n    <string name=\"medium\">Médio</string>\n    <string name=\"large\">Grande</string>\n    <string name=\"text_size\">Tamanho do texto</string>\n    <string name=\"add_a_reminder\">Adicionar lembrete</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"backup_export\">Exportar backup</string>\n    <string name=\"undo\">Desfazer</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notas</string>\n    <string name=\"title_create\">Nova nota</string>\n    <string name=\"timemachine\">Linha do tempo</string>\n    <string name=\"time\">Horário</string>\n    <string name=\"done\">Pronto</string>\n    <string name=\"saved\">Salvo</string>\n    <string name=\"menu_save_and_add\">Salvar e abrir nova nota</string>\n    <string name=\"menu_delete\">Eliminar</string>\n    <string name=\"menu_deletelist\">Apagar lista</string>\n    <string name=\"menu_sync\">Sincronizar</string>\n    <string name=\"menu_share\">Compartilhar</string>\n    <string name=\"menu_preferences\">Configurações</string>\n    <string name=\"menu_createlist\">Criar lista</string>\n    <string name=\"menu_clearcompleted\">Apagar concluídas</string>\n    <string name=\"menu_setdefaultlist\">Definir como lista padrão</string>\n    <string name=\"menu_managelists\">Gerenciar listas</string>\n    <string name=\"add_item\">Adicionar item</string>\n    <string name=\"delete_question\">Apagar?</string>\n    <string name=\"delete_items_message\">Tem certeza que deseja deletar estes itens?</string>\n    <string name=\"delete_item_message\">Tem certeza que deseja deletar este item?</string>\n    <string name=\"delete_list_message\">Tem certeza que deseja deletar esta lista?</string>\n    <string name=\"editor_due_date_hint\">Data limite</string>\n    <string name=\"editor_title_hint\">Título</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Editar nota</string>\n    <string name=\"default_style\">Estilo padrão</string>\n    <string name=\"show_items_as_tasks\">Mostrar itens como tarefas</string>\n    <string name=\"show_items_as_notes\">Mostrar itens como notas</string>\n    <string name=\"sort_list_default\">Ordenação padrão</string>\n    <string name=\"sort_list_alphabetical\">Ordenar alfabeticamente</string>\n    <string name=\"sort_list_due\">Ordenar por data limite</string>\n    <string name=\"sort_list_updated\">Ordenar por última alteração</string>\n    <string name=\"sort_list_manual\">Ordenar manualmente</string>\n    <string name=\"search_description\">Notas e tarefas</string>\n    <string name=\"archive\">Arquivar</string>\n    <string name=\"restore\">Restaurar</string>\n    <string name=\"restore_to\">Restaurar para</string>\n    <string name=\"search_hint\">Procurar</string>\n    <string name=\"settings_theme\">Tema atual</string>\n    <string name=\"settings_theme_dialog\">Usar tema</string>\n    <string name=\"settings_lang\">Idioma </string>\n    <string name=\"settings_summary_theme_black\">Preto</string>\n    <string name=\"settings_summary_theme_dark\">Escuro</string>\n    <string name=\"settings_summary_theme_light\">Claro</string>\n    <string name=\"settings_summary_theme_classic\">Clássico</string>\n    <string name=\"settings_cat_appearance\">Aparência</string>\n    <string name=\"preference_preview_text\">É assim que o texto irá aparecer</string>\n    <string name=\"settings_account_title\">Selecione uma conta</string>\n    <string name=\"settings_account_summary\">Clique para alterar a conta</string>\n    <string name=\"settings_cat_syncing\">Sincronização</string>\n    <string name=\"show_from_all_lists\">Todas as listas</string>\n    <string name=\"deleted\">Apagado</string>\n    <string name=\"repeat\">Repetir</string>\n    <string name=\"once\">Uma vez</string>\n    <string name=\"permission_read_label\">ler as notas e as listas</string>\n    <string name=\"permission_read_desc\">Permite que o aplicativo leia as suas notas e listas</string>\n    <string name=\"permission_write_label\">modificar as notas e as listas</string>\n    <string name=\"permission_write_desc\">Permite que a aplicação insira, atualize e apague notas e listas</string>\n    <string name=\"settings_list_dialog\">Selecione uma lista</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"import_data_question\">Importar dados?</string>\n    <string name=\"import_started\">Importando dados…</string>\n    <string name=\"imported_result\">Importado %1$d notas de %2$d listas</string>\n    <string name=\"import_error\">Algo deu errado: %s</string>\n    <string name=\"lock_note\">Bloquear nota</string>\n    <string name=\"unlock_note\">Desbloquear nota</string>\n    <string name=\"locked\">Bloqueada</string>\n    <string name=\"unlocked\">Desbloqueada</string>\n    <string name=\"password_required\">Senha necessária</string>\n    <string name=\"select_account\">Selecione uma conta</string>\n    <string name=\"password\">Senha</string>\n    <string name=\"password_set\">Definir senha</string>\n    <string name=\"password_cleared\">Senha apagada</string>\n    <string name=\"passwords_dont_match\">As senhas não combinam</string>\n    <string name=\"password_incorrect\">Senha errada</string>\n    <string name=\"enter_password\">Digite a senha</string>\n    <string name=\"enter_new_password\">Digite a nova senha</string>\n    <string name=\"confirm_new_password\">Confirme a nova senha</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"clear_password\">Apagar senha</string>\n    <string name=\"password_info\">Definir uma senha vai exigir que digite a mesma para mostrar todas as notas bloqueadas. Esta só vai proteger o campo da nota ficando ainda desprotegido o título e o prazo. Não existe qualquer encriptação da nota então ainda poderá ser vista no Google Mail/Calendário. Se esquecer a senha, sincronize com o Google e em seguida, reinstale o aplicativo.</string>\n    <string name=\"about\">Sobre</string>\n    <string name=\"sync_failed\">Sincronização falhou</string>\n    <string name=\"sync_login_failed\">Login falhou, não foi possível se conectar  com o Google Tasks</string>\n    <string name=\"completed\">Concluído </string>\n    <string name=\"date_header_overdue\">Atrasado</string>\n    <string name=\"date_header_today\">Hoje </string>\n    <string name=\"date_header_tomorrow\">Amanhã </string>\n    <string name=\"date_header_future\">Mais tarde </string>\n    <string name=\"date_header_none\">Sem data</string>\n    <string name=\"date_header_completed\">Completado</string>\n    <string name=\"please_select_note\">Por favor, selecione ou crie uma nota</string>\n    <string name=\"please_create_note\">Por favor, crie uma nota</string>\n    <string name=\"hide_checkbox\">Esconder checkbox</string>\n    <string name=\"hide_checkbox_summary_on\">Checkbox será escondida</string>\n    <string name=\"hide_checkbox_summary_off\">Checkbox será mostrada</string>\n    <string name=\"hide_date\">Esconder o prazo</string>\n    <string name=\"item_max_height\">Altura máxima das notas/tarefas</string>\n    <string name=\"long_date_format\">Formato de data extendido</string>\n    <string name=\"short_date_format\">Formato de data compacto</string>\n    <string name=\"localedefault\">Localização padrão</string>\n    <string name=\"hide_header\">Esconder todo o cabeçalho do widget</string>\n    <string name=\"transparency\">Transparência</string>\n    <string name=\"notes_shortcut\">Atalho para notas</string>\n    <string name=\"shortcut_help1\">Se marcado, o atalho irá criar uma nova nota na lista selecionada e abrir o editor. Se desmarcado, o atalho irá abrir a lista selecionada.</string>\n    <string name=\"loading_widget\">Carregando widget…</string>\n    <string name=\"default_list\">Lista padrão</string>\n    <string name=\"please_type_before_reminder\">Por favor, insira algum texto antes de definir um lembrete</string>\n    <string name=\"notecopied_one\">Uma nota copiada</string>\n    <string name=\"notecopied_other\">%d notas copiadas</string>\n    <string name=\"notedeleted_one\">Uma nota apagada</string>\n    <string name=\"notedeleted_other\">%d notas apagadas</string>\n    <string name=\"selected_one\">Uma nota selecionada</string>\n    <string name=\"selected_other\">%d notas selecionadas</string>\n    <string name=\"background_sync\">Sincronização em segundo plano</string>\n    <string name=\"background_sync_info\">Sincroniza de hora em hora</string>\n    <string name=\"sync_on_change\">Sincronizar após modificações</string>\n    <string name=\"sync_on_change_info\">Sincronizar imediatamente depois que a nota foi modificada</string>\n    <string name=\"sync_on_start\">Sincronização na inicialização do app</string>\n    <string name=\"sync_on_start_info\">Irá sincronizar no máximo a cada 5 minutos</string>\n    <string name=\"snooze\">Adiar o lembrete</string>\n    <string name=\"silent\">Silêncio</string>\n    <string name=\"sound\">Som</string>\n    <string name=\"vibrate\">Vibrar</string>\n    <string name=\"reminders\">Lembretes</string>\n    <string name=\"standard\">Padrão</string>\n    <string name=\"notification_prio_high\">Alta: No topo, expandida</string>\n    <string name=\"notification_prio_low\">Baixa: Escondida, minimizada</string>\n    <string name=\"priority\">Prioridade</string>\n    <string name=\"notifications\">Notificações</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restringir tarefas a uma lista específica</string>\n    <string name=\"dashclock_show_overdue_tasks\">Mostrar tarefas atrasadas</string>\n    <string name=\"dashclock_overdue_tasks_show\">Tarefas atrasadas serão exibidas</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Tarefas vencidas serão ocultas</string>\n    <string name=\"dashclock_pref_header_general\">Geral</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limitar a tarefas que vencem mais antes</string>\n    <string name=\"dashclock_today\">Hoje</string>\n    <string name=\"dashclock_tomorrow\">Amanhã</string>\n    <string name=\"dashclock_next7\">Próximos 7 dias</string>\n    <string name=\"dashclock_anytime\">A qualquer hora</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Irá mostrar o máximo possível</string>\n    <string name=\"dashclock_show_only_the_next_task\">Mostrar somente a próxima tarefa</string>\n    <string name=\"dashclock_header_shown\">O nome da lista será exibido em negrito. Se \\\"Todas as listas\\\" for selecionado, irá mostrar \\\"Tarefas\\\"</string>\n    <string name=\"dashclock_first_task_shown\">O título da primeira tarefa será exibido em negrito</string>\n    <string name=\"dashclock_display_header\">Mostrar cabeçalho</string>\n    <string name=\"delete_completed_tasks_question\">Excluir todas tarefas completadas?</string>\n    <string name=\"medium\">Médio</string>\n    <string name=\"tasks\">Tarefas</string>\n    <string name=\"all_tasks\">Todas as tarefas</string>\n    <string name=\"backup_file_not_found\">Não foi possível encontrar o arquivo do backup</string>\n    <string name=\"show_elements_as\">Mostrar itens como:</string>\n    <string name=\"next_month\">Próximo mês</string>\n    <string name=\"next_year\">Próximo ano</string>\n    <string name=\"order_notes_by\">Ordernar notas por:</string>\n    <string name=\"select_all\">Selecionar tudo</string>\n    <string name=\"showcase_tutorial_description\">Temos um tutorial online. Encontre-o nas configurações</string>\n    <string name=\"welcome_note_title\">Bem-vindo(a)!</string>\n    <string name=\"welcome_note_row_2\">Para começar, abra isto.</string>\n    <string name=\"show_completed_notes\">Mostrar notas completas</string>\n    <string name=\"move\">Mover</string>\n    <string name=\"move_to\">Mover para</string>\n    <string name=\"moved_x_to_list\">Movido %1$d para %2$s</string>\n    <string name=\"next_5_days\">Próximos cinco dias</string>\n    <string name=\"next_n_days\">Próximos %d dias</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Negrito</string>\n    <string name=\"title_style\">Estilo de título</string>\n    <string name=\"title_font\">Fonte do título</string>\n    <string name=\"clickable_links\">Links clicáveis</string>\n    <string name=\"text\">Texto</string>\n    <string name=\"add_a_reminder\">Adicionar lembrete</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"backup_import\">Importar backup</string>\n    <string name=\"backup_export\">Exportar backup</string>\n    <string name=\"backup_export_msg\">Exportar todas as notas para %1$s?</string>\n    <string name=\"backup_import_success\">Backup importado</string>\n    <string name=\"directory\">Pasta</string>\n    <string name=\"cannot_write_to_directory\">Não é possível escrever na pasta</string>\n    <string name=\"bigger_titles_summary\">O título é a primeira linha</string>\n    <string name=\"undo\">Desfazer</string>\n    <string name=\"notifications_enabled\">Notificações estão visíveis</string>\n    <string name=\"version\">Versão: %s</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Configurações</string>\n    <string name=\"backup_import_failed\">Não foi possível ler o arquivo do backup</string>\n    <string name=\"backup_export_success\">Backup exportado</string>\n    <string name=\"sd_card\">Cartão SD</string>\n    <string name=\"bigger_titles\">Títulos maiores</string>\n    <string name=\"not_selected_yet\">Ainda não selecionado</string>\n    <string name=\"account\">Conta</string>\n    <string name=\"unavailable_chose_directory\">Indisponível. Por favor, escolha uma pasta primeiro</string>\n    <string name=\"this_week\">Esta semana</string>\n    <string name=\"italic\">Itálico</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"small\">Pequeno</string>\n    <string name=\"large\">Grande</string>\n    <string name=\"text_size\">Tamanho do texto</string>\n    <string name=\"enable_sync\">Ativar sincronização</string>\n    <string name=\"navigation_drawer_open\">Abrir barra de navegação</string>\n    <string name=\"navigation_drawer_close\">Fechar barra de navegação</string>\n    <string name=\"no_sync_method_chosen\">Você não escolheu um método de sincronização</string>\n    <string name=\"choose_backup_folder\">Escolha uma pasta para backup</string>\n    <string name=\"disable_battery_optimizations\">Desativar otimização da bateria</string>\n    <string name=\"libraries_used\">Bibliotecas</string>\n    <string name=\"feature_is_WIP\">Estamos trabalhando neste recurso.</string>\n    <string name=\"notification_channel_name\">Lembretes para notas</string>\n    <string name=\"notification_channel_description\">Mostra o conteúdo da nota no momento escolhido no lembrete</string>\n    <string name=\"for_older_devices\">Para dispositivos Android mais antigos</string>\n    <string name=\"permission_denied\">Não foi possível prosseguir: você negou a permissão</string>\n    <string name=\"app_about_translations\">Ajude a traduzir o aplicativo em https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"app_about_git_repo\">Este aplicativo é um software de código aberto, disponível em https://github.com/spacecowboy/NotePad</string>\n    <string name=\"new_default_set\">Novo padrão definido</string>\n    <string name=\"lists\">Listas</string>\n    <string name=\"always\">Sempre</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-pt-rPT/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notas</string>\n    <string name=\"title_create\">Nova nota</string>\n    <string name=\"menu_delete\">Eliminar</string>\n    <string name=\"menu_deletelist\">Apagar lista</string>\n    <string name=\"menu_sync\">Sincronizar</string>\n    <string name=\"menu_share\">Partilhar</string>\n    <string name=\"menu_preferences\">Configurações</string>\n    <string name=\"menu_createlist\">Criar lista</string>\n    <string name=\"menu_clearcompleted\">Apagar concluídas</string>\n    <string name=\"menu_setdefaultlist\">Definir como lista padrão</string>\n    <string name=\"menu_managelists\">Gerir listas</string>\n    <string name=\"editor_due_date_hint\">Data limite</string>\n    <string name=\"editor_title_hint\">Título</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"resolve_edit\">Editar nota</string>\n    <string name=\"search_hint\">Procurar</string>\n    <string name=\"settings_theme\">Tema atual</string>\n    <string name=\"settings_theme_dialog\">Usar tema</string>\n    <string name=\"settings_lang\">Idioma </string>\n    <string name=\"settings_summary_theme_black\">Preto</string>\n    <string name=\"settings_summary_theme_dark\">Escuro</string>\n    <string name=\"settings_summary_theme_light\">Claro</string>\n    <string name=\"settings_cat_appearance\">Aparência</string>\n    <string name=\"preference_preview_text\">É assim que o texto irá aparecer</string>\n    <string name=\"settings_account_title\">Selecione uma conta</string>\n    <string name=\"settings_account_summary\">Clique para alterar a conta</string>\n    <string name=\"settings_cat_syncing\">Sincronização</string>\n    <string name=\"show_from_all_lists\">Todas as listas</string>\n    <string name=\"deleted\">Apagado</string>\n    <string name=\"permission_read_label\">ler as notas e as listas</string>\n    <string name=\"permission_read_desc\">Permite que a aplicação leia as suas notas e listas</string>\n    <string name=\"permission_write_label\">modificar as notas e as listas</string>\n    <string name=\"permission_write_desc\">Permite que a aplicação insira, atualize e apague notas e listas</string>\n    <string name=\"settings_list_dialog\">Selecione uma lista</string>\n    <string name=\"settings_list\">Lista</string>\n    <!--\n        Use few if language need it. In English is the same.\n        <item quantity=\"few\">Copied 2 notes.</item>\n    -->\n    <string name=\"lock_note\">Bloquear nota</string>\n    <string name=\"unlock_note\">Desbloquear nota</string>\n    <string name=\"locked\">Bloqueada</string>\n    <string name=\"unlocked\">Desbloqueada</string>\n    <string name=\"password_required\">Palavra-passe necessária</string>\n    <string name=\"select_account\">Selecione uma conta</string>\n    <string name=\"password_set\">Definir palavra-passe</string>\n    <string name=\"password_cleared\">Palavra-passe apagada</string>\n    <string name=\"passwords_dont_match\">As palavras-passe não combinam</string>\n    <string name=\"password_incorrect\">Palavra-passe errada</string>\n    <string name=\"enter_password\">Digite a palavra-passe</string>\n    <string name=\"enter_new_password\">Digite a nova palavra-passe</string>\n    <string name=\"confirm_new_password\">Confirme a nova palavra-passe</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"clear_password\">Apagar palavra-passe</string>\n    <string name=\"password_info\">Definir uma password vai exigir que digite a mesmapara mostrar todas as notas bloqueadas. Esta só vai proteger o campo da nota ficando aindadesprotegido o título e data. Não existe qualquer encriptação da nota para que esta possaser vista no Google Mail/Calendário. Se esquecer a password, sincronize com o Google eem seguida, reinstale a aplicação.</string>\n    <string name=\"about\">Sobre</string>\n    <string name=\"completed\">Concluído </string>\n    <string name=\"date_header_today\">Hoje </string>\n    <string name=\"date_header_tomorrow\">Amanhã </string>\n    <string name=\"date_header_future\">Mais tarde </string>\n    <string name=\"date_header_none\">Sem data</string>\n    <!-- settings string keys -->\n    <!-- \"localtime\" is replaced at runtime with 12/24 hour clock -->\n    <string name=\"sync_failed\">Sincronização falhou</string>\n    <string name=\"date_header_completed\">Completado</string>\n    <string name=\"hide_date\">Esconder o prazo</string>\n    <string name=\"hide_header\">Esconder todo o cabeçalho do widget</string>\n    <string name=\"transparency\">Transparência</string>\n    <string name=\"notes_shortcut\">Atalho para notas</string>\n    <string name=\"vibrate\">Vibrar</string>\n    <string name=\"reminders\">Lembretes</string>\n    <string name=\"standard\">Padrão</string>\n    <string name=\"notification_prio_high\">Alta: No topo, expandida</string>\n    <string name=\"dashclock_show_overdue_tasks\">Mostrar tarefas atrasadas</string>\n    <string name=\"timemachine\">Linha do tempo</string>\n    <string name=\"time\">Horário</string>\n    <string name=\"done\">Pronto</string>\n    <string name=\"saved\">Gravado</string>\n    <string name=\"menu_save_and_add\">Gravar e abrir nova nota</string>\n    <string name=\"add_item\">Adicionar item</string>\n    <string name=\"delete_question\">Apagar\\?</string>\n    <string name=\"delete_items_message\">Apagar estes itens\\?</string>\n    <string name=\"show_items_as_notes\">Mostrar itens como notas</string>\n    <string name=\"delete_item_message\">Apagar este item\\?</string>\n    <string name=\"default_style\">Estilo padrão</string>\n    <string name=\"show_items_as_tasks\">Mostrar itens como tarefas</string>\n    <string name=\"search_description\">Notas e tarefas</string>\n    <string name=\"archive\">Arquivar</string>\n    <string name=\"restore\">Restaurar</string>\n    <string name=\"restore_to\">Restaurar para</string>\n    <string name=\"settings_summary_theme_classic\">Clássico</string>\n    <string name=\"repeat\">Repetir</string>\n    <string name=\"once\">Uma vez</string>\n    <string name=\"import_data_question\">Importar dados\\?</string>\n    <string name=\"import_error\">Algo deu errado: %s</string>\n    <string name=\"password\">Palavra-passe</string>\n    <string name=\"date_header_overdue\">Atrasado</string>\n    <string name=\"please_select_note\">Por favor, selecione ou crie uma nota</string>\n    <string name=\"hide_checkbox\">Esconder checkbox</string>\n    <string name=\"please_create_note\">Por favor, crie uma nota</string>\n    <string name=\"loading_widget\">A carregar o widget…</string>\n    <string name=\"default_list\">Lista padrão</string>\n    <string name=\"notedeleted_other\">%d notas apagadas</string>\n    <string name=\"selected_other\">%d notas selecionadas</string>\n    <string name=\"background_sync\">Sincronização em segundo plano</string>\n    <string name=\"background_sync_info\">Sincroniza de hora em hora</string>\n    <string name=\"sync_on_change\">Sincronizar após modificações</string>\n    <string name=\"sync_on_change_info\">Sincronizar imediatamente depois que a nota foi modificada</string>\n    <string name=\"notifications\">Notificações</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Restringir tarefas a uma lista específica</string>\n    <string name=\"dashclock_pref_header_general\">Geral</string>\n    <string name=\"dashclock_tomorrow\">Amanhã</string>\n    <string name=\"dashclock_next7\">Próximos 7 dias</string>\n    <string name=\"dashclock_anytime\">A qualquer hora</string>\n    <string name=\"dashclock_show_only_the_next_task\">Mostrar somente a próxima tarefa</string>\n    <string name=\"dashclock_display_header\">Mostrar cabeçalho</string>\n    <string name=\"sort_list_due\">Data limite</string>\n    <string name=\"item_max_height\">Altura máxima das notas/tarefas</string>\n    <string name=\"localedefault\">Localização padrão</string>\n    <string name=\"sync_on_start\">Sincronização na inicialização do app</string>\n    <string name=\"snooze\">Adiar o lembrete</string>\n    <string name=\"silent\">Silêncio</string>\n    <string name=\"sound\">Som</string>\n    <string name=\"notification_prio_low\">Baixa: Escondida, minimizada</string>\n    <string name=\"priority\">Prioridade</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limitar a tarefas que vencem mais antes</string>\n    <string name=\"dashclock_today\">Hoje</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Configurações</string>\n    <string name=\"notecopied_other\">%d notas copiadas</string>\n    <string name=\"delete_list_message\">Apagar esta lista\\?</string>\n    <string name=\"delete_completed_tasks_question\">Apagar todas tarefas completadas?</string>\n    <string name=\"lists\">Listas</string>\n    <string name=\"always\">Sempre</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_updated\">Última atualização</string>\n    <string name=\"sort_list_manual\">Manual</string>\n    <string name=\"notifications_enabled\">Notificações estão visíveis</string>\n    <string name=\"enable_sync\">Ativar sincronização</string>\n    <string name=\"bold\">Negrito</string>\n    <string name=\"italic\">Itálico</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"title_style\">Estilo de título</string>\n    <string name=\"title_font\">Fonte do título</string>\n    <string name=\"welcome_note_title\">Bem-vindo(a)!</string>\n    <string name=\"new_default_set\">Novo padrão definido</string>\n    <string name=\"no_sync_method_chosen\">Não escolheu um método de sincronização</string>\n    <string name=\"choose_backup_folder\">Escolha uma pasta para backup</string>\n    <string name=\"notification_channel_name\">Lembretes para notas</string>\n    <string name=\"feature_is_WIP\">Estamos trabalhando neste recurso.</string>\n    <string name=\"this_week\">Esta semana</string>\n    <string name=\"directory\">Pasta</string>\n    <string name=\"cannot_write_to_directory\">Não é possível escrever na pasta</string>\n    <string name=\"app_about_git_repo\">Esta app é um software de código aberto, disponível em https://github.com/spacecowboy/NotePad</string>\n    <string name=\"app_about_translations\">Ajude a traduzir a app em https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"disable_battery_optimizations\">Desativar otimização da pilha</string>\n    <string name=\"libraries_used\">Bibliotecas</string>\n    <string name=\"unavailable_chose_directory\">Indisponível. Por favor, escolha uma pasta primeiro</string>\n    <string name=\"move\">Mover</string>\n    <string name=\"all_tasks\">Todas as tarefas</string>\n    <string name=\"add_a_reminder\">Adicionar lembrete</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"navigation_drawer_open\">Abrir barra de navegação</string>\n    <string name=\"navigation_drawer_close\">Fechar barra de navegação</string>\n    <string name=\"notification_channel_description\">Mostra o conteúdo da nota no momento escolhido no lembrete</string>\n    <string name=\"permission_denied\">Não foi possível prosseguir: você negou a permissão</string>\n    <string name=\"for_older_devices\">Para dispositivos Android mais antigos</string>\n    <string name=\"not_selected_yet\">Ainda não selecionado</string>\n    <string name=\"order_notes_by\">Ordernar notas por:</string>\n    <string name=\"show_elements_as\">Mostrar itens como:</string>\n    <string name=\"version\">Versão: %s</string>\n    <string name=\"next_month\">Próximo mês</string>\n    <string name=\"next_year\">Próximo ano</string>\n    <string name=\"select_all\">Selecionar tudo</string>\n    <string name=\"showcase_tutorial_description\">Temos um tutorial online. Encontre-o nas configurações</string>\n    <string name=\"welcome_note_row_2\">Para começar, abra isto.</string>\n    <string name=\"show_completed_notes\">Mostrar notas completas</string>\n    <string name=\"move_to\">Mover para</string>\n    <string name=\"moved_x_to_list\">Movido %1$d para %2$s</string>\n    <string name=\"account\">Conta</string>\n    <string name=\"next_5_days\">Próximos cinco dias</string>\n    <string name=\"next_n_days\">Próximos %d dias</string>\n    <string name=\"tasks\">Tarefas</string>\n    <string name=\"clickable_links\">Ligações clicáveis</string>\n    <string name=\"small\">Pequeno</string>\n    <string name=\"medium\">Médio</string>\n    <string name=\"large\">Grande</string>\n    <string name=\"text_size\">Tamanho do texto</string>\n    <string name=\"text\">Texto</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"backup_import\">Importar backup</string>\n    <string name=\"backup_export\">Exportar backup</string>\n    <string name=\"backup_export_msg\">Exportar todas as notas para %1$s?</string>\n    <string name=\"backup_import_success\">Backup importado</string>\n    <string name=\"backup_file_not_found\">Não foi possível encontrar o ficheiro do backup</string>\n    <string name=\"backup_import_failed\">Não foi possível ler o ficheiro do backup</string>\n    <string name=\"backup_export_success\">Backup exportado</string>\n    <string name=\"sd_card\">Cartão SD</string>\n    <string name=\"bigger_titles\">Títulos maiores</string>\n    <string name=\"bigger_titles_summary\">O título é a primeira linha</string>\n    <string name=\"undo\">Desfazer</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ro/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Note</string>\n    <string name=\"title_create\">Notă nouă</string>\n    <string name=\"timemachine\">Mașina timpului</string>\n    <string name=\"time\">Timp</string>\n    <string name=\"done\">Terminat</string>\n    <string name=\"saved\">Salvat</string>\n    <string name=\"menu_save_and_add\">Salvează si adauga nota</string>\n    <string name=\"menu_delete\">Șterge</string>\n    <string name=\"menu_deletelist\">Șterge lista</string>\n    <string name=\"menu_sync\">Actualizează</string>\n    <string name=\"menu_share\">Partajează</string>\n    <string name=\"menu_preferences\">Setări</string>\n    <string name=\"menu_createlist\">Creează lista</string>\n    <string name=\"menu_clearcompleted\">Șterge notele finalizate</string>\n    <string name=\"menu_setdefaultlist\">Stabilește ca listă implicită</string>\n    <string name=\"new_default_set\">Set implicit nou</string>\n    <string name=\"menu_managelists\">Gestionează listele</string>\n    <string name=\"add_item\">Adaugă element</string>\n    <string name=\"delete_question\">Șterge?</string>\n    <string name=\"delete_items_message\">Ești sigur că vrei să ștergi aceste elemente?</string>\n    <string name=\"delete_item_message\">Ești sigur că vrei să ștergi acest element?</string>\n    <string name=\"delete_list_message\">Ești sigur că vrei să ștergi lista?</string>\n    <string name=\"editor_due_date_hint\">Termen</string>\n    <string name=\"editor_title_hint\">Titlu</string>\n    <string name=\"editor_note_hint\">Notă</string>\n    <string name=\"resolve_edit\">Modifică nota</string>\n    <string name=\"default_style\">Stilul implicit</string>\n    <string name=\"show_items_as_tasks\">Afișază elementele ca atribuții</string>\n    <string name=\"show_items_as_notes\">Arată elementele ca notițe</string>\n    <string name=\"sort_list_default\">Ordine de sortare implicită</string>\n    <string name=\"sort_list_alphabetical\">Sortare alfabetică</string>\n    <string name=\"sort_list_due\">Sortare după dată</string>\n    <string name=\"sort_list_updated\">Sortare după ultima actualizare</string>\n    <string name=\"sort_list_manual\">Sortare manuală</string>\n    <string name=\"search_description\">Notițe și atribuții</string>\n    <string name=\"archive\">Arhivă</string>\n    <string name=\"restore\">Restabilire</string>\n    <string name=\"restore_to\">Restabilire la</string>\n    <string name=\"search_hint\">Caută</string>\n    <string name=\"settings_theme\">Tema actuală</string>\n    <string name=\"settings_theme_dialog\">Folosește tema</string>\n    <string name=\"settings_lang\">Limba</string>\n    <string name=\"settings_summary_theme_black\">Negru</string>\n    <string name=\"settings_summary_theme_dark\">Întunecat</string>\n    <string name=\"settings_summary_theme_light\">Luminat</string>\n    <string name=\"settings_summary_theme_classic\">Clasic</string>\n    <string name=\"settings_cat_appearance\">Aspect</string>\n    <string name=\"preference_preview_text\">Așa va fi afișat editorul</string>\n    <string name=\"settings_account_title\">Alege un cont</string>\n    <string name=\"settings_account_summary\">Clic pentru a schimba contul</string>\n    <string name=\"settings_cat_syncing\">Sincronizare</string>\n    <string name=\"show_from_all_lists\">Toate listele</string>\n    <string name=\"lists\">Liste</string>\n    <string name=\"deleted\">Ștearsă</string>\n    <string name=\"repeat\">Repetă</string>\n    <string name=\"once\">O dată</string>\n    <string name=\"always\">Întotdeauna</string>\n    <string name=\"permission_read_label\">citește notele și listele</string>\n    <string name=\"permission_read_desc\">Permite aplicației să citească notele și listele aferente</string>\n    <string name=\"permission_write_label\">modifică notele și litele</string>\n    <string name=\"permission_write_desc\">Permite aplicației să adauge, actualizeze și șteargă note și liste</string>\n    <string name=\"settings_list_dialog\">Alege o listă</string>\n    <string name=\"settings_list\">Listă</string>\n    <string name=\"drag_to_timetravel\">Trage pentru a călatori în timp</string>\n    <string name=\"import_data_question\">Importă datele?</string>\n    <string name=\"import_started\">Se importă datele…</string>\n    <string name=\"imported_result\">S-a importat %1$d notițe în %2$d liste</string>\n    <string name=\"import_error\">Ceva nu a funcționat corect: %s</string>\n    <string name=\"move\">Mută</string>\n    <string name=\"move_to\">Mută la</string>\n    <string name=\"moved_x_to_list\">Mutat %1$d la %2$s</string>\n    <string name=\"lock_note\">Blochează nota</string>\n    <string name=\"unlock_note\">Deblochează nota</string>\n    <string name=\"locked\">Blocată</string>\n    <string name=\"unlocked\">Deblocată</string>\n    <string name=\"password_required\">Este necesară parola</string>\n    <string name=\"select_account\">Alege un cont</string>\n    <string name=\"password\">Parolă</string>\n    <string name=\"password_set\">Parola a fost salvată</string>\n    <string name=\"password_cleared\">Parola a fost ștearsă</string>\n    <string name=\"passwords_dont_match\">Parolele nu se potrivesc</string>\n    <string name=\"password_incorrect\">Parolă incorectă</string>\n    <string name=\"enter_password\">Introdu parola</string>\n    <string name=\"enter_new_password\">Introdu o parolă nouă</string>\n    <string name=\"confirm_new_password\">Confirmă parola nouă</string>\n    <string name=\"apply\">Se aplică</string>\n    <string name=\"clear_password\">Șterge parola</string>\n    <string name=\"password_info\">Blocarea unei notițe limitează accesul la vizualizarea și modificarea ei. Poți bloca notițe în mod individual. Notițele sunt blocate doar aici și rămân accesibile pe alte aparate.</string>\n    <string name=\"about\">Despre</string>\n    <string name=\"sync_failed\">Sincronizarea a eșuat</string>\n    <string name=\"sync_login_failed\">Autentificarea a eșuat, eroare conexiune la Sarcini Google</string>\n    <string name=\"completed\">Finalizat</string>\n    <string name=\"date_header_overdue\">Depășit</string>\n    <string name=\"date_header_today\">Azi</string>\n    <string name=\"date_header_tomorrow\">Mâine</string>\n    <string name=\"date_header_future\">Mai târziu</string>\n    <string name=\"date_header_none\">Fără dată</string>\n    <string name=\"date_header_completed\">Finalizare</string>\n    <string name=\"next_5_days\">Următoarele 5 zile</string>\n    <string name=\"next_n_days\">Următoarele %d zile</string>\n    <string name=\"this_week\">Săptămâna aceasta</string>\n    <string name=\"please_select_note\">Creează sau alege o notă</string>\n    <string name=\"please_create_note\">Creează o notă</string>\n    <string name=\"hide_checkbox\">Ascunde bifa</string>\n    <string name=\"hide_checkbox_summary_on\">Bifa va fi ascunsă</string>\n    <string name=\"hide_checkbox_summary_off\">Bifa va fi afișată</string>\n    <string name=\"hide_date\">Ascunde termenul</string>\n    <string name=\"item_max_height\">Înălțimea maximă a notițelor/sarcinilor</string>\n    <string name=\"long_date_format\">Format extensiv</string>\n    <string name=\"short_date_format\">Format compact</string>\n    <string name=\"select_date\">Selectează date</string>\n    <string name=\"localedefault\">Implicit telefon</string>\n    <!-- settings string keys -->\n    <string name=\"hide_header\">Ascunde tot antetul</string>\n    <string name=\"transparency\">Transparență</string>\n    <string name=\"notes_shortcut\">Scurtăturile notițelor</string>\n    <string name=\"shortcut_help1\">Dacă bifezi, scurtătura va crea o notă nouă în lista aleasă și va deschide editorul. Dacă nu bifezi, scurtătura va deschide lista aleasă.</string>\n    <string name=\"loading_widget\">Se încarcă…</string>\n    <string name=\"default_list\">Listă implicită</string>\n    <string name=\"please_type_before_reminder\">Te rugăm să introduci un text înainte de a stabili un memento</string>\n    <string name=\"notecopied_one\">Notiță 1 copiată</string>\n    <string name=\"notecopied_other\">Notițe %d copiate</string>\n    <string name=\"notedeleted_one\">Notița 1 ștearsă</string>\n    <string name=\"notedeleted_other\">Notițe %d șterse</string>\n    <string name=\"selected_one\">Notiță 1 selectată</string>\n    <string name=\"selected_other\">Notițe %d selectate</string>\n    <string name=\"background_sync\">Fundal sincronizat</string>\n    <string name=\"background_sync_info\">Se sincronizează o dată pe oră</string>\n    <string name=\"sync_on_change\">Sincronizare modificări</string>\n    <string name=\"sync_on_change_info\">Programează o sincronizare la scurt timp după ce o notiță a fost schimbată</string>\n    <string name=\"sync_on_start\">Sincronizare la pornirea aplicației</string>\n    <string name=\"sync_on_start_info\">Se va sincroniza la fiecare 5 minute</string>\n    <string name=\"snooze\">Amână</string>\n    <string name=\"silent\">Silențios</string>\n    <string name=\"sound\">Sunet</string>\n    <string name=\"vibrate\">Vibrează</string>\n    <string name=\"reminders\">Memento-uri</string>\n    <string name=\"standard\">Standard</string>\n    <string name=\"notification_prio_high\">Înalt: În partea de sus, expandat</string>\n    <string name=\"notification_prio_low\">Jos: Ascuns, minimizat</string>\n    <string name=\"priority\">Prioritate</string>\n    <string name=\"notifications\">Notificări</string>\n    <!-- \"localtime\" is replaced at runtime with 12/24 hour clock -->\n    <string name=\"tasks\">Sarcini</string>\n    <string name=\"all_tasks\">Toate sarcinile</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Limitează la sarcini în</string>\n    <string name=\"dashclock_show_overdue_tasks\">Arată sarcinile restante</string>\n    <string name=\"dashclock_overdue_tasks_show\">Sarcinile întârziate vor fi afișate</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Sarcinile întârziate vor fi ascunse</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Setări NoNonsense Notes</string>\n    <string name=\"dashclock_pref_header_general\">General</string>\n    <string name=\"dashclock_due_upper_limit_title\">Limitează la sarcinile care trebuie executate mai târziu</string>\n    <string name=\"dashclock_today\">Astăzi</string>\n    <string name=\"dashclock_tomorrow\">Mâine</string>\n    <string name=\"dashclock_next7\">Următoarele 7 zile</string>\n    <string name=\"dashclock_anytime\">Oricând</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">O să arate cât mai mult posibile</string>\n    <string name=\"dashclock_show_only_the_next_task\">Arată doar următoarea sarcină</string>\n    <string name=\"dashclock_header_shown\">Numele listei va fi afișat îngroșat</string>\n    <string name=\"dashclock_first_task_shown\">Numele primei sarcini din listă va fi îngroșat</string>\n    <string name=\"dashclock_display_header\">Arată numele listei</string>\n    <string name=\"editor\">Editor</string>\n    <string name=\"bold\">Îngroșat</string>\n    <string name=\"italic\">Înclinat</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"title_style\">Stilul titlului</string>\n    <string name=\"title_font\">Fontul titlului</string>\n    <string name=\"body_font\">Fontul corpului textului</string>\n    <string name=\"clickable_links\">Faceți click pe link-uri</string>\n    <string name=\"small\">Mic</string>\n    <string name=\"medium\">Mediu</string>\n    <string name=\"large\">Mare</string>\n    <string name=\"text_size\">Dimensiunea textului</string>\n    <string name=\"text\">Text</string>\n    <string name=\"add_a_reminder\">Adaugă un memento</string>\n    <string name=\"backup\">Copie de rezervă</string>\n    <string name=\"backup_import\">Importă copia de rezervă</string>\n    <string name=\"backup_export\">Exportă copia de rezervă</string>\n    <string name=\"backup_import_msg\">Încerci să imporți copiile de rezervă din %1$s? Aceasta va șterge baza de date curentă.</string>\n    <string name=\"backup_export_msg\">Exportă toate notițele la %1$s?</string>\n    <string name=\"backup_import_success\">Copiile de rezervă importate cu succes</string>\n    <string name=\"backup_file_not_found\">Copiile de rezervă nu au fost găsite</string>\n    <string name=\"backup_import_failed\">Nu s-a putut citi copia de rezervă</string>\n    <string name=\"backup_export_success\">Copiile de rezervă au fost exportate cu succes</string>\n    <string name=\"backup_export_failed\">Nu se poate scrie pe copia de rezervă</string>\n    <string name=\"sd_card\">Card SD</string>\n    <string name=\"sd_card_sync\">Sincronizare Card SD</string>\n    <string name=\"sd_card_summary\">Sarcinile sunt oglindite pe cardul SD. Modificările sunt sincronizate în ambele sensuri. Rețineți că ștergerea fișierelor va șterge sarcinile din aplicație!</string>\n    <string name=\"directory\">Director</string>\n    <string name=\"cannot_write_to_directory\">Nu se poate scrie în director</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Заметки</string>\n    <string name=\"title_create\">Новая запись</string>\n    <string name=\"timemachine\">Машина времени</string>\n    <string name=\"time\">Время</string>\n    <string name=\"done\">Готово</string>\n    <string name=\"saved\">Сохранено</string>\n    <string name=\"menu_save_and_add\">Записать и открыть новую заметку</string>\n    <string name=\"menu_delete\">Удалить</string>\n    <string name=\"menu_deletelist\">Удалить список</string>\n    <string name=\"menu_sync\">Синхронизировать</string>\n    <string name=\"menu_share\">Отправить</string>\n    <string name=\"menu_preferences\">Настройки</string>\n    <string name=\"menu_createlist\">Создать список</string>\n    <string name=\"menu_clearcompleted\">Очистить завершённые</string>\n    <string name=\"menu_setdefaultlist\">Задать как список по умолчанию</string>\n    <string name=\"new_default_set\">Новые установки по умолчанию</string>\n    <string name=\"menu_managelists\">Управление списками</string>\n    <string name=\"add_item\">Добавить</string>\n    <string name=\"delete_question\">Удалить?</string>\n    <string name=\"delete_items_message\">Удалить эти заметки?</string>\n    <string name=\"delete_item_message\">Удалить эту заметку?</string>\n    <string name=\"delete_list_message\">Удалить этот список?</string>\n    <string name=\"editor_due_date_hint\">Дата окончания</string>\n    <string name=\"editor_title_hint\">Заголовок</string>\n    <string name=\"editor_note_hint\">Заметка</string>\n    <string name=\"resolve_edit\">Редактировать запись</string>\n    <string name=\"default_style\">Стиль по умолчанию</string>\n    <string name=\"show_items_as_tasks\">Как задачи</string>\n    <string name=\"show_items_as_notes\">Как заметки</string>\n    <string name=\"sort_list_default\">Сортировка по умолчанию</string>\n    <string name=\"sort_list_alphabetical\">А-Я</string>\n    <string name=\"sort_list_due\">По дате</string>\n    <string name=\"sort_list_updated\">По последнему обновлению</string>\n    <string name=\"sort_list_manual\">Вручную</string>\n    <string name=\"search_description\">Заметки как задачи</string>\n    <string name=\"archive\">Архив</string>\n    <string name=\"restore\">Восстановить</string>\n    <string name=\"restore_to\">Восстановить в</string>\n    <string name=\"search_hint\">Поиск</string>\n    <string name=\"settings_theme\">Текущая тема</string>\n    <string name=\"settings_theme_dialog\">Использовать тему</string>\n    <string name=\"settings_lang\">Язык</string>\n    <string name=\"settings_summary_theme_black\">Черная</string>\n    <string name=\"settings_summary_theme_dark\">Тёмная</string>\n    <string name=\"settings_summary_theme_light\">Светлая</string>\n    <string name=\"settings_summary_theme_classic\">Классическая</string>\n    <string name=\"settings_cat_appearance\">Внешний вид</string>\n    <string name=\"preference_preview_text\">Так будет выглядеть текст при редактировании</string>\n    <string name=\"settings_account_title\">Выберите учётную запись</string>\n    <string name=\"settings_account_summary\">Нажмите для смены учётной записи</string>\n    <string name=\"settings_cat_syncing\">Синхронизация</string>\n    <string name=\"show_from_all_lists\">Все списки</string>\n    <string name=\"lists\">Списки</string>\n    <string name=\"deleted\">Удалено</string>\n    <string name=\"repeat\">Повторить</string>\n    <string name=\"once\">Один раз</string>\n    <string name=\"always\">Всегда</string>\n    <string name=\"permission_read_label\">читать записи и списки</string>\n    <string name=\"permission_read_desc\">Разрешить приложению читать записи и связанные списки</string>\n    <string name=\"permission_write_label\">изменять записи и списки</string>\n    <string name=\"permission_write_desc\">Разрешить приложению добавлять, изменять и удалять записи и списки</string>\n    <string name=\"settings_list_dialog\">Выберите список</string>\n    <string name=\"settings_list\">Список</string>\n    <string name=\"import_data_question\">Импортировать данные?</string>\n    <string name=\"import_started\">Импорт данных…</string>\n    <string name=\"move\">Переместить</string>\n    <string name=\"move_to\">Переместить в</string>\n    <string name=\"moved_x_to_list\">Переместить %1$d в%2$s</string>\n    <string name=\"lock_note\">Заблокировать запись</string>\n    <string name=\"unlock_note\">Разблокировать запись</string>\n    <string name=\"locked\">Заблокировано</string>\n    <string name=\"unlocked\">Разблокировано</string>\n    <string name=\"password_required\">Требуется пароль</string>\n    <string name=\"select_account\">Выберите учётную запись</string>\n    <string name=\"password\">Пароль</string>\n    <string name=\"password_set\">Пароль установлен</string>\n    <string name=\"password_cleared\">Пароль очищен</string>\n    <string name=\"passwords_dont_match\">Пароли не совпадают</string>\n    <string name=\"password_incorrect\">Неверный пароль</string>\n    <string name=\"enter_password\">Введите пароль</string>\n    <string name=\"enter_new_password\">Введите новый пароль</string>\n    <string name=\"confirm_new_password\">Подтвердите новый пароль</string>\n    <string name=\"apply\">Применить</string>\n    <string name=\"clear_password\">Очистить пароль</string>\n    <string name=\"password_info\">Любую заметку можно заблокировать, ограничив её просмотр и изменение. Она остаётся полностью доступной на других устройствах.</string>\n    <string name=\"about\">О программе</string>\n    <string name=\"sync_failed\">Ошибка синхронизации</string>\n    <string name=\"sync_login_failed\">Не удалось войти в систему, чтобы использовать Google Tasks</string>\n    <string name=\"completed\">Завершено</string>\n    <string name=\"date_header_overdue\">Сверх срока</string>\n    <string name=\"date_header_today\">Сегодня</string>\n    <string name=\"date_header_tomorrow\">Завтра</string>\n    <string name=\"date_header_future\">Позднее</string>\n    <string name=\"date_header_none\">Без даты</string>\n    <string name=\"date_header_completed\">Завершено</string>\n    <string name=\"next_5_days\">Следующие 5 дней</string>\n    <string name=\"this_week\">На этой неделе</string>\n    <string name=\"please_select_note\">Выберите или создайте заметку, пожалуйста</string>\n    <string name=\"please_create_note\">Создайте заметку, пожалуйста</string>\n    <string name=\"hide_checkbox\">Скрыть отметку</string>\n    <string name=\"hide_checkbox_summary_on\">Отметка скрыта</string>\n    <string name=\"hide_checkbox_summary_off\">Отметка показана</string>\n    <string name=\"hide_date\">Скрыть дату окончания</string>\n    <string name=\"item_max_height\">Максимальная высота заметки/задачи</string>\n    <string name=\"long_date_format\">Формат длинной даты, для панели</string>\n    <string name=\"short_date_format\">Короткий формат даты, для списка</string>\n    <string name=\"select_date\">Выберите дату</string>\n    <string name=\"localedefault\">Язык по-умолчанию</string>\n    <string name=\"hide_header\">Скрыть весь заголовок виджета</string>\n    <string name=\"transparency\">Прозрачность</string>\n    <string name=\"notes_shortcut\">Ярлыки заметки</string>\n    <string name=\"shortcut_help1\">Создаёт ярлык для открытия новой заметки в выбранном списке с открытым текстовым редактором.</string>\n    <string name=\"loading_widget\">Загрузка виджета…</string>\n    <string name=\"default_list\">Список по умолчанию</string>\n    <string name=\"please_type_before_reminder\">Пожалуйста, введите текст перед добавлением напоминания</string>\n    <string name=\"notecopied_one\">Скопирована одна заметка</string>\n    <string name=\"notecopied_other\">Скопировано %d заметки</string>\n    <string name=\"notedeleted_one\">Удалена одна заметка</string>\n    <string name=\"notedeleted_other\">Выделено %d заметки</string>\n    <string name=\"selected_one\">Выделена одна заметка</string>\n    <string name=\"selected_other\">Выделено %d заметки</string>\n    <string name=\"background_sync\">Фоновая синхр</string>\n    <string name=\"background_sync_info\">Синхронизировать каждый час</string>\n    <string name=\"sync_on_change\">Синхр. при изменениях</string>\n    <string name=\"sync_on_change_info\">Синхронизировать через некоторое время после изменения заметки</string>\n    <string name=\"sync_on_start\">Синхр. при запуске</string>\n    <string name=\"sync_on_start_info\">Ограничивает синхронизацию раз в 5 минут</string>\n    <string name=\"snooze\">Вздремнуть</string>\n    <string name=\"silent\">Без звука</string>\n    <string name=\"sound\">Мелодия</string>\n    <string name=\"vibrate\">Вибрация</string>\n    <string name=\"reminders\">Напоминания</string>\n    <string name=\"standard\">Обычный</string>\n    <string name=\"notification_prio_high\">Высокий: сверху, расширенный</string>\n    <string name=\"notification_prio_low\">Низкий: скрытый, свернутый</string>\n    <string name=\"priority\">Приоритет</string>\n    <string name=\"notifications\">Уведомления</string>\n    <string name=\"tasks\">Заметки</string>\n    <string name=\"all_tasks\">Все заметки</string>\n    <string name=\"dashclock_show_overdue_tasks\">Показать просроченные задачи</string>\n    <string name=\"dashclock_overdue_tasks_show\">Отображены просроченные задачи</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Просроченные задачи не отображены</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Настройки</string>\n    <string name=\"dashclock_pref_header_general\">Главная</string>\n    <string name=\"dashclock_today\">Сегодня</string>\n    <string name=\"dashclock_tomorrow\">Завтра</string>\n    <string name=\"dashclock_next7\">На семь дней вперед</string>\n    <string name=\"dashclock_anytime\">В любое время</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Отображать так много, как возможно</string>\n    <string name=\"dashclock_show_only_the_next_task\">Показывать только следующую задачу</string>\n    <string name=\"dashclock_display_header\">Отображать заголовок</string>\n    <string name=\"delete_completed_tasks_question\">Удалить все выполненные задачи\\?</string>\n    <string name=\"import_error\">Что-то пошло не так: %s</string>\n    <string name=\"notification_channel_name\">Напоминания для заметок</string>\n    <string name=\"feature_is_WIP\">Эта функция находится в разработке.</string>\n    <string name=\"permission_denied\">Нельзя продолжить: вы отказали в разрешении</string>\n    <string name=\"use_exact_alarms\">Использовать точные напоминания</string>\n    <string name=\"for_older_devices\">Для старых устройств Android</string>\n    <string name=\"app_about_git_repo\">Это приложение является копилефтным свободным программным обеспечением, доступным по адресу https://github.com/spacecowboy/NotePad</string>\n    <string name=\"app_about_license\">Лицензия GPLv3+, https://www.gnu.org/licenses</string>\n    <string name=\"drag_to_timetravel\">Перемещайте для изменения времени</string>\n    <string name=\"navigation_drawer_open\">Открыть навигационное меню</string>\n    <string name=\"navigation_drawer_close\">Закрыть навигационное меню</string>\n    <string name=\"no_sync_method_chosen\">Вы не выбрали метод синхронизации</string>\n    <string name=\"prefs_improved_reliability\">Предпочтения для повышения надёжности</string>\n    <string name=\"app_about_translations\">Помочь с переводом https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"app_about_donations\">Мы принимаем пожертвования. Чтобы поддержать нашу работу, ищите %1$s в %2$s</string>\n    <string name=\"disable_android_hibernation\">Отключение спящего режима Android</string>\n    <string name=\"msg_enable_notifications\">Перейдите в настройки -&gt; уведомления, чтобы включить уведомления</string>\n    <string name=\"notifications_visibility\">Отображение уведомлений</string>\n    <string name=\"notifications_enabled\">Уведомления отображаются</string>\n    <string name=\"version\">Версия: %s</string>\n    <string name=\"select_all\">Выбрать всё</string>\n    <string name=\"showcase_tutorial_title\">Чувствуете себя потерянным?</string>\n    <string name=\"showcase_tutorial_description\">У вас есть руководство по использованию приложения. Найдите в настройках</string>\n    <string name=\"next_month\">Следующий месяц</string>\n    <string name=\"next_year\">Следующий год</string>\n    <string name=\"welcome_note_row_3\">Это приложение имеет подробное руководство. Вы можете найти его на</string>\n    <string name=\"unsupported_readonly_file\">Неподдерживаемый файл, доступный только для чтения: %s</string>\n    <string name=\"show_completed_notes\">Показать завершённые заметки</string>\n    <string name=\"next_n_days\">Следующие %d дней</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Ограничить задачи в</string>\n    <string name=\"italic\">Курсив</string>\n    <string name=\"title_style\">Стиль заголовка</string>\n    <string name=\"body_font\">Основной шрифт</string>\n    <string name=\"clickable_links\">Переходить по ссылкам по нажатию</string>\n    <string name=\"small\">Маленький</string>\n    <string name=\"medium\">Средний</string>\n    <string name=\"text_size\">Размер текста</string>\n    <string name=\"backup\">Резервное копирование</string>\n    <string name=\"backup_import\">Импорт копии</string>\n    <string name=\"backup_export\">Экспорт копии</string>\n    <string name=\"backup_import_success\">Резервная копия импортирована</string>\n    <string name=\"sd_card\">SD-карта</string>\n    <string name=\"sd_card_sync\">Синхронизация с SD-картой</string>\n    <string name=\"undo\">Отменить</string>\n    <string name=\"file_picker_not_available\">Выбор файла недоступен</string>\n    <string name=\"choose_backup_folder\">Выберите папку для резервного копирования</string>\n    <string name=\"show_elements_as\">Отображать элементы как:</string>\n    <string name=\"tutorial_online\">Руководство по использованию</string>\n    <string name=\"welcome_note_title\">Добро пожаловать!</string>\n    <string name=\"welcome_note_row_2\">Откройте это, чтобы начать.</string>\n    <string name=\"imported_result\">Импортировано %1$d заметок в %2$d списков</string>\n    <string name=\"exact_alarms_summary\">Потребляет больше заряда батареи, чтобы надёжнее показывать напоминания об уведомлениях</string>\n    <string name=\"battery_optimizations_active\">Оптимизация батареи включена</string>\n    <string name=\"battery_optimizations_inactive\">Оптимизация батареи отключена</string>\n    <string name=\"allow_exact_reminders\">Разрешить точные напоминания</string>\n    <string name=\"allow_exact_reminders_summary\">Откройте страницу, чтобы разрешить этому приложению отправлять напоминания надежнее. Это необходимо только с Android 12</string>\n    <string name=\"overwritten_in_newer_systems\">Может быть отменено определёнными настройками канала уведомлений</string>\n    <string name=\"notification_channel_settings\">Настройки канала уведомлений</string>\n    <string name=\"open_channel_settings_description\">Откройте страницу, чтобы изменить настройки уведомлений для этого приложения. Эти настройки отменяют любые выбранные ранее</string>\n    <string name=\"libraries_used\">Используемые библиотеки</string>\n    <string name=\"msg_hibernation_already_off\">Спящий режим Android уже выключен, вам это не нужно</string>\n    <string name=\"app_about_dev_team\">Создано Jonas Kalderstam и поддерживается Campello Manuel</string>\n    <string name=\"app_about_bugreports\">Сообщать о багах и проблемах https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"dashclock_due_upper_limit_title\">Ограничить задачи, подлежащие выполнению не позднее</string>\n    <string name=\"editor\">Редактор</string>\n    <string name=\"dashclock_header_shown\">Жирное имя списка</string>\n    <string name=\"dashclock_first_task_shown\">Жирный заголовок первой задачи</string>\n    <string name=\"bold\">Жирный</string>\n    <string name=\"text\">Текст</string>\n    <string name=\"add_a_reminder\">Добавить напоминание</string>\n    <string name=\"backup_export_msg\">Экспортировать все заметки в %1$s?</string>\n    <string name=\"backup_file_not_found\">Не удалось найти файл резервной копии с именем NoNonsenseNotes_Backup.json</string>\n    <string name=\"backup_import_failed\">Не удалось прочитать файл резервной копии NoNonsenseNotes_Backup.json</string>\n    <string name=\"backup_export_success\">Резервная копия экспортирована</string>\n    <string name=\"backup_export_failed\">Не удалось записать в файл резервной копии</string>\n    <string name=\"directory\">Папка</string>\n    <string name=\"directory_summary_msg\">Файлы сохраняются в %s \\nк которым вы можете получить доступ с помощью приложения менеджера файлов. Удаление приложения также приведет к удалению этих файлов: чтобы сохранить ваши заметки, создайте резервную копию</string>\n    <string name=\"cannot_write_to_directory\">Не удаётся записать в папку</string>\n    <string name=\"bigger_titles\">Огромные заголовки</string>\n    <string name=\"sorting\">Сортировка</string>\n    <string name=\"notification_channel_description\">Отображает содержание заметки на момент выбранного напоминания</string>\n    <string name=\"disable_battery_optimizations\">Отключение оптимизации батареи</string>\n    <string name=\"disable_android_hibernation_desc\">Эта функция, которая делает приложение менее надёжным. Она может блокировать ваши напоминания. Откройте это, найдите что-то вроде \\\"приостановить активность приложения, если не используется\\\" и отключите</string>\n    <string name=\"notifications_blocked\">Уведомления не отображаются, поэтому вы не увидите напоминаний. Нажмите здесь, найдите категорию разрешений и включите уведомления для этого приложения</string>\n    <string name=\"unavailable_chose_directory\">Недоступно. Пожалуйста, сначала выберите папку</string>\n    <string name=\"enable_sync_desc\">Включает функции синхронизации и позволяет выбрать источник синхронизации. Резервные копии не затрагиваются</string>\n    <string name=\"changelog_online\">Список изменений</string>\n    <string name=\"normal\">Нормальный</string>\n    <string name=\"title_font\">Шрифт заголовка</string>\n    <string name=\"backup_import_msg\">Импортировать резервную копию из %1$s? Это приведёт к очистке текущей базы данных.</string>\n    <string name=\"account\">Аккаунт</string>\n    <string name=\"large\">Большой</string>\n    <string name=\"sd_card_summary\">Задачи остаются одинаковыми между приложением и SD-картой. Удаление файлов приводит к удалению задач в приложении!</string>\n    <string name=\"bigger_titles_summary\">Заголовок это первая строка</string>\n    <string name=\"not_selected_yet\">Ещё не выбрана</string>\n    <string name=\"enable_sync\">Включить синхронизацию</string>\n    <string name=\"order_notes_by\">Сортировать заметки по:</string>\n    <string name=\"canceled_note_locked\">Отменено: заметка заблокирована</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Poznámky</string>\n    <string name=\"title_create\">Nová poznámka</string>\n    <string name=\"timemachine\">Časostroj </string>\n    <string name=\"time\">Čas</string>\n    <string name=\"done\">Hotovo</string>\n    <string name=\"saved\">Uložené</string>\n    <string name=\"menu_save_and_add\">Uložiť a pridať</string>\n    <string name=\"menu_delete\">Vymazať</string>\n    <string name=\"menu_deletelist\">Vymazať zoznam</string>\n    <string name=\"menu_sync\">Obnoviť</string>\n    <string name=\"menu_share\">Zdielať</string>\n    <string name=\"menu_preferences\">Nastavenia</string>\n    <string name=\"menu_createlist\">Vytvoriť zoznam</string>\n    <string name=\"menu_clearcompleted\">Vymazať hotové</string>\n    <string name=\"menu_setdefaultlist\">Nastaviť ako predvolený zoznam</string>\n    <string name=\"menu_managelists\">Upraviť zoznm</string>\n    <string name=\"add_item\">Pridať položku</string>\n    <string name=\"delete_question\">Vymazať?</string>\n    <string name=\"delete_items_message\">Určite vymazať tieto položky?</string>\n    <string name=\"delete_item_message\">Určite vymazať túto položku?</string>\n    <string name=\"delete_list_message\">Určite vymazať tento zoznam?</string>\n    <string name=\"editor_due_date_hint\">Deadline</string>\n    <string name=\"editor_title_hint\">Titulok</string>\n    <string name=\"editor_note_hint\">Poznámka</string>\n    <string name=\"resolve_edit\">Uprav poznámku</string>\n    <string name=\"default_style\">Predvolený štýl</string>\n    <string name=\"show_items_as_tasks\">Ukázať položky ako úlohy</string>\n    <string name=\"show_items_as_notes\">Ukázať položky ako poznámky</string>\n    <string name=\"sort_list_default\">Predvolené zoradenie</string>\n    <string name=\"sort_list_alphabetical\">Zoradiť abecedne</string>\n    <string name=\"sort_list_due\">Zoradiť podľa deadlinu</string>\n    <string name=\"sort_list_updated\">Zoradiť podľa poslednej aktualizácie</string>\n    <string name=\"sort_list_manual\">Zoradiť manuálne</string>\n    <string name=\"search_description\">Poznámky a úlohy</string>\n    <string name=\"archive\">Archív</string>\n    <string name=\"restore\">Vrátiť</string>\n    <string name=\"restore_to\">Vrátiť do</string>\n    <string name=\"search_hint\">Hľadať</string>\n    <string name=\"settings_theme\">Aktuálna téma</string>\n    <string name=\"settings_theme_dialog\">Použiť tému</string>\n    <string name=\"settings_lang\">Jazyk</string>\n    <string name=\"settings_summary_theme_black\">Čierna</string>\n    <string name=\"settings_summary_theme_dark\">Tmavá</string>\n    <string name=\"settings_summary_theme_light\">Bledá</string>\n    <string name=\"settings_summary_theme_classic\">Klasická</string>\n    <string name=\"settings_cat_appearance\">Vzhľad</string>\n    <string name=\"preference_preview_text\">Takto bude vypadať textový editor </string>\n    <string name=\"settings_account_title\">Vybrať konto</string>\n    <string name=\"settings_account_summary\">Kliknúť pre prepnutie konta</string>\n    <string name=\"settings_cat_syncing\">Synchronizácia</string>\n    <string name=\"show_from_all_lists\">Všetky zoznamy</string>\n    <string name=\"deleted\">Vymazané</string>\n    <string name=\"repeat\">Opakovať</string>\n    <string name=\"once\">Jeden krát</string>\n    <string name=\"always\">Vždy</string>\n    <string name=\"permission_read_label\">Čítať poznámky a zoznamy</string>\n    <string name=\"permission_read_desc\">Povoliť aplikácii čítať poznámky a zoznamy</string>\n    <string name=\"permission_write_label\">Upravovať poznámky a zoznamy</string>\n    <string name=\"permission_write_desc\">Povoliť aplikácii vkladať, upravovať a vymazávať poznámky a zoznamy</string>\n    <string name=\"settings_list_dialog\">Vybrať zoznam</string>\n    <string name=\"settings_list\">Zoznam</string>\n    <string name=\"drag_to_timetravel\">Ťahať pre cestovanie časom</string>\n    <string name=\"import_data_question\">Importovať dáta?</string>\n    <string name=\"import_started\">Importujem dáta…</string>\n    <string name=\"move\">Presunúť</string>\n    <string name=\"move_to\">Presunúť do</string>\n    <string name=\"moved_x_to_list\">Presunuté %1$d do %2$s</string>\n    <string name=\"lock_note\">Zamknúť poznámku</string>\n    <string name=\"unlock_note\">Odomknúť poznámku</string>\n    <string name=\"locked\">Uzamknuté</string>\n    <string name=\"unlocked\">Odomknuté</string>\n    <string name=\"password_required\">Potrebné heslo</string>\n    <string name=\"select_account\">Vybrať konto</string>\n    <string name=\"password\">Heslo</string>\n    <string name=\"password_set\">Heslo nastavené</string>\n    <string name=\"password_cleared\">Heslo vymazané</string>\n    <string name=\"passwords_dont_match\">Heslá sa nezhodujú</string>\n    <string name=\"password_incorrect\">Nesprávne heslo</string>\n    <string name=\"enter_password\">Vložiť heslo</string>\n    <string name=\"enter_new_password\">Vložiť nové heslo</string>\n    <string name=\"confirm_new_password\">Potvrdiť nové heslo</string>\n    <string name=\"apply\">Použiť</string>\n    <string name=\"clear_password\">Vymazať heslo</string>\n    <string name=\"password_info\">Uzamknutie poznámky obmedzuje prístup k prezeraniu a upravovaniu poznámok. Uzamykáte poznámky osobitne. Poznámky sú chránené iba na tomto zariadení a plne prístupné na iných zariadeniach.</string>\n    <string name=\"about\">O aplikácii</string>\n    <string name=\"sync_failed\">Synchronizácia zlyhala</string>\n    <string name=\"sync_login_failed\">Prihlásenie zlyhalo, nepodarilo sa pripojiť k Google Task</string>\n    <string name=\"completed\">Hotové</string>\n    <string name=\"date_header_overdue\">Po deadline</string>\n    <string name=\"date_header_today\">Dnes</string>\n    <string name=\"date_header_tomorrow\">Zajtra</string>\n    <string name=\"date_header_future\">Neskôr</string>\n    <string name=\"date_header_none\">Bez dátumu</string>\n    <string name=\"date_header_completed\">Hotové</string>\n    <string name=\"please_select_note\">Prosím vyberte alebo vytvorte zoznam</string>\n    <string name=\"please_create_note\">Prosím vytvorte poznámku</string>\n    <string name=\"hide_checkbox\">Skryť checkbox</string>\n    <string name=\"hide_checkbox_summary_on\">Checkboxy budú skryté</string>\n    <string name=\"hide_checkbox_summary_off\">Checkboxy budú zobrazené</string>\n    <string name=\"hide_date\">Skryť deadline</string>\n    <string name=\"item_max_height\">Maximálna výška poznámok/úloh</string>\n    <string name=\"long_date_format\">Dlhý formát dátumu </string>\n    <string name=\"short_date_format\">Krátky formát dátumu</string>\n    <string name=\"select_date\">Vybrať dátum</string>\n    <string name=\"localedefault\">Predvolená pozícia</string>\n    <string name=\"hide_header\">Skryť celú hlavičku</string>\n    <string name=\"transparency\">Priehľadnosť</string>\n    <string name=\"notes_shortcut\">Skratka pre poznámky</string>\n    <string name=\"shortcut_help1\">Ak je označené, skratka vytvorí novú poznámku vo vybranom zozname a otvorí editor. Ak nieje označené, skratka otvorí vybraný zoznam.</string>\n    <string name=\"loading_widget\">Načítam widget…</string>\n    <string name=\"default_list\">Predvolený zoznam</string>\n    <string name=\"please_type_before_reminder\">Napíšte text pred nastavením pripomienky</string>\n    <string name=\"notecopied_one\">Skopírovaná 1 poznámka</string>\n    <string name=\"notecopied_other\">Skopírované %d poznámky</string>\n    <string name=\"notedeleted_one\">Vymazaná 1 poznámka</string>\n    <string name=\"notedeleted_other\">Vymazane %d poznámky</string>\n    <string name=\"selected_one\">Vybraná 1 poznámka</string>\n    <string name=\"selected_other\">Vybrané %d poznámky</string>\n    <string name=\"background_sync\">Synchronizácia na pozadí</string>\n    <string name=\"background_sync_info\">Synchronizovať raz za hodinu</string>\n    <string name=\"sync_on_change\">Synchronizovať po zmene</string>\n    <string name=\"sync_on_change_info\">Naplánovať synchronizáciu krátko po zmene poznámky</string>\n    <string name=\"sync_on_start\">Synchronizovať aplikáciu po štarte</string>\n    <string name=\"sync_on_start_info\">Najčastejšie sa bude synchronizovať každých 5 minút</string>\n    <string name=\"snooze\">Odložiť</string>\n    <string name=\"silent\">Ticho</string>\n    <string name=\"sound\">Zvuk</string>\n    <string name=\"vibrate\">Vibrovať</string>\n    <string name=\"reminders\">Pripomienky</string>\n    <string name=\"standard\">Štandardné </string>\n    <string name=\"notification_prio_high\">Vysoká: Navrchu, rozbalené</string>\n    <string name=\"notification_prio_low\">Nízka: Skryté, minimalizované</string>\n    <string name=\"priority\">Priorita</string>\n    <string name=\"notifications\">Notifikácie</string>\n    <string name=\"tasks\">Úlohy</string>\n    <string name=\"dashclock_show_overdue_tasks\">Ukázať úlohy po deadline</string>\n    <string name=\"dashclock_overdue_tasks_show\">Úlohy po deadline budú zobrazené</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Úlohy po deadline budú skryté</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense Notes nastavenia</string>\n    <string name=\"dashclock_pref_header_general\">Hlavné</string>\n    <string name=\"dashclock_today\">Dnes</string>\n    <string name=\"dashclock_tomorrow\">Zajtra</string>\n    <string name=\"dashclock_next7\">Nasledujúcich 7 dní</string>\n    <string name=\"dashclock_anytime\">Kedykoľvek</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Ukáže toľko koľko je možné</string>\n    <string name=\"dashclock_show_only_the_next_task\">Ukázať iba následujúcu úlohu </string>\n    <string name=\"dashclock_header_shown\">Názov zoznamu bude zobrazený tučným písmom</string>\n    <string name=\"dashclock_first_task_shown\">Názov prvej úlohy bude zobrazený tučným písmom</string>\n    <string name=\"dashclock_display_header\">Zobraziť názov zoznamu</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Opombe</string>\n    <string name=\"title_create\">Nova opomba</string>\n    <string name=\"timemachine\">Časovni stroj</string>\n    <string name=\"time\">Čas</string>\n    <string name=\"done\">Končano</string>\n    <string name=\"saved\">Shranjene</string>\n    <string name=\"menu_save_and_add\">Shrani in dodaj opombo</string>\n    <string name=\"menu_delete\">Izbriši</string>\n    <string name=\"menu_deletelist\">Izbriši seznam</string>\n    <string name=\"menu_sync\">Sinhronizacija</string>\n    <string name=\"menu_share\">Delež</string>\n    <string name=\"menu_preferences\">Nastavitve</string>\n    <string name=\"menu_createlist\">Začni nov seznam</string>\n    <string name=\"menu_clearcompleted\">Počistite izpolnjen</string>\n    <string name=\"menu_setdefaultlist\">Nastavi kot privzeti seznam</string>\n    <string name=\"new_default_set\">Nov privzeti nabor</string>\n    <string name=\"menu_managelists\">Uredi seznam</string>\n    <string name=\"add_item\">Dodaj element</string>\n    <string name=\"delete_question\">Izbrišite?</string>\n    <string name=\"delete_items_message\">Ali ste prepričani, da želite izbrisati te elemente?</string>\n    <string name=\"delete_item_message\">Ali ste prepričani, da želite izbrisati ta element?</string>\n    <string name=\"delete_list_message\">Ali ste prepričani, da želite izbrisati seznam?</string>\n    <string name=\"editor_due_date_hint\">Datum zapadlosti</string>\n    <string name=\"editor_title_hint\">Naslov</string>\n    <string name=\"editor_note_hint\">Opomba</string>\n    <string name=\"resolve_edit\">Uredi opombo</string>\n    <string name=\"default_style\">Privzeti slog</string>\n    <string name=\"show_items_as_tasks\">Pokaži elemente kot opravila</string>\n    <string name=\"show_items_as_notes\">Pokaži elemente, kot opombe</string>\n    <string name=\"sort_list_default\">Privzeti vrstni red</string>\n    <string name=\"sort_list_alphabetical\">Naročite po abecedi</string>\n    <string name=\"sort_list_due\">Naročite po ustreznem datum</string>\n    <string name=\"sort_list_updated\">Naročite po zadnji posodobljeni</string>\n    <string name=\"sort_list_manual\">Naročite ročno</string>\n    <string name=\"search_description\">Opombe in opravila</string>\n    <string name=\"archive\">Arhiv</string>\n    <string name=\"restore\">Obnovitev</string>\n    <string name=\"restore_to\">Obnovi v </string>\n    <string name=\"search_hint\">Išči</string>\n    <string name=\"settings_theme\">Trenutna tema</string>\n    <string name=\"settings_theme_dialog\">Uporabi temo</string>\n    <string name=\"settings_lang\">Jezik</string>\n    <string name=\"settings_summary_theme_black\">Črna</string>\n    <string name=\"settings_summary_theme_dark\">Temno</string>\n    <string name=\"settings_summary_theme_light\">Svetlobe</string>\n    <string name=\"settings_summary_theme_classic\">Klasičen</string>\n    <string name=\"settings_cat_appearance\">Videz</string>\n    <string name=\"preference_preview_text\">To je, kako bo videti besedilo urednik</string>\n    <string name=\"settings_account_title\">Izberite račun</string>\n    <string name=\"settings_account_summary\">Dotik preiti račun</string>\n    <string name=\"settings_cat_syncing\">Sinhronizacija</string>\n    <string name=\"show_from_all_lists\">Vsi seznami</string>\n    <string name=\"lists\">Seznami</string>\n    <string name=\"deleted\">Črta</string>\n    <string name=\"repeat\">Ponovi</string>\n    <string name=\"once\">Enkrat</string>\n    <string name=\"always\">Vedno</string>\n    <string name=\"permission_read_label\">Preberite opombe in seznami</string>\n    <string name=\"permission_read_desc\">Omogoča uporabo preberite opombe in sorodnih seznamov</string>\n    <string name=\"permission_write_label\">Spreminjanje opombe in seznami</string>\n    <string name=\"permission_write_desc\">Omogoča uporabo za vstavljanje, posodabljanje in brisanje opombe in sezname</string>\n    <string name=\"settings_list_dialog\">Izberite seznam</string>\n    <string name=\"settings_list\">Seznam</string>\n    <string name=\"drag_to_timetravel\">Povlecite čas potovanja</string>\n    <string name=\"import_data_question\">Uvoz podatkov?</string>\n    <string name=\"import_started\">Uvažanje podatkov…</string>\n    <string name=\"imported_result\">Uvoženi %1$d opombe v %2$d seznami</string>\n    <string name=\"import_error\">Nekaj je šlo narobe: %s</string>\n    <string name=\"move\">Premakni</string>\n    <string name=\"move_to\">Premakni v</string>\n    <string name=\"moved_x_to_list\">Preseli %1$d v %2$s</string>\n    <string name=\"lock_note\">Zakleni opomba</string>\n    <string name=\"unlock_note\">Odkleni opomba</string>\n    <string name=\"locked\">Zaklenjena</string>\n    <string name=\"unlocked\">Odklenjena</string>\n    <string name=\"password_required\">Zahtevano je geslo</string>\n    <string name=\"select_account\">Izberite račun</string>\n    <string name=\"password\">Geslo</string>\n    <string name=\"password_set\">Nastavljeno geslo</string>\n    <string name=\"password_cleared\">Očiščeno geslo</string>\n    <string name=\"passwords_dont_match\">Gesli se ne ujemata</string>\n    <string name=\"password_incorrect\">Nepravilno geslo</string>\n    <string name=\"enter_password\">Vnesite geslo</string>\n    <string name=\"enter_new_password\">Vnesite novo geslo</string>\n    <string name=\"confirm_new_password\">Potrditev novega gesla</string>\n    <string name=\"apply\">Uporablja</string>\n    <string name=\"clear_password\">Jasno geslo</string>\n    <string name=\"password_info\">Zaklepanje Opomba omejuje dostop do sanjarstvo ter ublažiti to. Zaklenete opombe na individualni osnovi. Opombe so le zaščiteni tukaj in ostanejo popolnoma dostopen na druge naprave.</string>\n    <string name=\"about\">O</string>\n    <string name=\"sync_failed\">Sinhroniziranje ni uspelo</string>\n    <string name=\"sync_login_failed\">Prijava ni uspela, ni bilo mogoče vzpostaviti Google opravila</string>\n    <string name=\"completed\">Dokončana</string>\n    <string name=\"date_header_overdue\">Zapadle</string>\n    <string name=\"date_header_today\">Danes</string>\n    <string name=\"date_header_tomorrow\">Jutri</string>\n    <string name=\"date_header_future\">Kasneje</string>\n    <string name=\"date_header_none\">Brez datuma</string>\n    <string name=\"date_header_completed\">Dokončana</string>\n    <string name=\"next_5_days\">Naslednjih 5 dni</string>\n    <string name=\"next_n_days\">Naslednjih %d dneh</string>\n    <string name=\"this_week\">Ta teden</string>\n    <string name=\"please_select_note\">Ptosimo, da izberite ali ustvarite opombo</string>\n    <string name=\"please_create_note\">Prosimo, da ustvarite opombo</string>\n    <string name=\"hide_checkbox\">Skrij čekovno knjižico</string>\n    <string name=\"hide_checkbox_summary_on\">Čekovna knjižica hoteti obstati skriven</string>\n    <string name=\"hide_checkbox_summary_off\">Čekovna knjižica hoteti obstati razstava</string>\n    <string name=\"hide_date\">Skrij datum zapadlosti</string>\n    <string name=\"item_max_height\">Maks višina opombe/nalog</string>\n    <string name=\"long_date_format\">Dolga oblika datuma</string>\n    <string name=\"short_date_format\">Kratka oblika datuma</string>\n    <string name=\"select_date\">Izberite datum</string>\n    <string name=\"localedefault\">Privzete področne nastavitve</string>\n    <string name=\"hide_header\">Skriti celotno widget glave</string>\n    <string name=\"transparency\">Preglednost</string>\n    <string name=\"notes_shortcut\">Opombe bližnjico</string>\n    <string name=\"shortcut_help1\">Če je potrjeno, bližnjico ustvarite novo opombo v izbranem seznamu in odprite urejevalnik. Če bližnjica odpre izbrani seznam.</string>\n    <string name=\"loading_widget\">Nalaganje widget…</string>\n    <string name=\"default_list\">Privzeti seznam</string>\n    <string name=\"please_type_before_reminder\">Vnesite nekaj besedila, preden nastavite opomnik</string>\n    <string name=\"notecopied_one\">Opomba prekopirane 1</string>\n    <string name=\"notecopied_other\">Kopirane %d opombe</string>\n    <string name=\"notedeleted_one\">Opomba izbrisani 1</string>\n    <string name=\"notedeleted_other\">Izbrisati %d opombe</string>\n    <string name=\"selected_one\">Opomba izbrani 1</string>\n    <string name=\"selected_other\">Izbrati %d opombe</string>\n    <string name=\"background_sync\">Ozadju sinhronizacijo</string>\n    <string name=\"background_sync_info\">Sinhronizira enkrat na uro</string>\n    <string name=\"sync_on_change\">Sinhronizacijo na spremembe</string>\n    <string name=\"sync_on_change_info\">Urniki sinhrono, kmalu potem, ko je spremenil opombo</string>\n    <string name=\"sync_on_start\">Sync on application start</string>\n    <string name=\"sync_on_start_info\">Sinhronizira na večini vsakih 5 minut</string>\n    <string name=\"snooze\">Dremež</string>\n    <string name=\"silent\">Tiho</string>\n    <string name=\"sound\">Zvok</string>\n    <string name=\"vibrate\">Vibrirati</string>\n    <string name=\"reminders\">Opomniki</string>\n    <string name=\"standard\">Standardni</string>\n    <string name=\"notification_prio_high\">Visoka: Na vrhu, razširjen</string>\n    <string name=\"notification_prio_low\">Nizko: Skrit, minimirano</string>\n    <string name=\"priority\">Prednost</string>\n    <string name=\"notifications\">Obvestila</string>\n    <string name=\"tasks\">Naloge</string>\n    <string name=\"all_tasks\">Vsa opravila</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Omejiti naloge v</string>\n    <string name=\"dashclock_show_overdue_tasks\">Pokaži zapadla opravila</string>\n    <string name=\"dashclock_overdue_tasks_show\">Bo prikazan zapadla opravila</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Zapadla opravila bo skrit</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense opomba nastavitve</string>\n    <string name=\"dashclock_pref_header_general\">Splošno</string>\n    <string name=\"dashclock_due_upper_limit_title\">Omejitev naloge zaradi najkasneje</string>\n    <string name=\"dashclock_today\">Danes</string>\n    <string name=\"dashclock_tomorrow\">Jutri</string>\n    <string name=\"dashclock_next7\">Naslednjih 7 dni</string>\n    <string name=\"dashclock_anytime\">Kadarkoli</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Bo pokazati čim več</string>\n    <string name=\"dashclock_show_only_the_next_task\">Prikaz naslednjega opravila</string>\n    <string name=\"dashclock_header_shown\">Ime seznama, bo prikazan v krepko</string>\n    <string name=\"dashclock_first_task_shown\">Naziv prva naloga bo prikazan v krepko</string>\n    <string name=\"dashclock_display_header\">Prikazno ime seznama</string>\n    <string name=\"editor\">Urednik</string>\n    <string name=\"bold\">Krepko</string>\n    <string name=\"italic\">Ležeče</string>\n    <string name=\"normal\">Normalno</string>\n    <string name=\"title_style\">Slog naslova</string>\n    <string name=\"title_font\">Pisava v naslovu</string>\n    <string name=\"body_font\">Pisava telesa</string>\n    <string name=\"clickable_links\">Mogoče klikniti povezave</string>\n    <string name=\"small\">Mala</string>\n    <string name=\"medium\">Srednje</string>\n    <string name=\"large\">Velika</string>\n    <string name=\"text_size\">Velikost besedila</string>\n    <string name=\"text\">Besedilo</string>\n    <string name=\"add_a_reminder\">Dodaj opomnik</string>\n    <string name=\"backup\">Varnostno kopiranje</string>\n    <string name=\"backup_import\">Uvoz varnostne kopije</string>\n    <string name=\"backup_export\">Izvoz varnostne kopije</string>\n    <string name=\"backup_import_msg\">Poskusite uvoziti varnostno kopijo iz %1$s? To bo jasno v trenutni zbirki podatkov.</string>\n    <string name=\"backup_export_msg\">Izvozi vse opombe v %1$s?</string>\n    <string name=\"backup_import_success\">Varnostne kopije, ki so uspešno uvoženi</string>\n    <string name=\"backup_file_not_found\">Varnostne kopije datoteke ni bilo mogoče najti</string>\n    <string name=\"backup_import_failed\">Ni bilo mogoče prebrati datoteko varnostne kopije</string>\n    <string name=\"backup_export_success\">Varnostno kopiranje, ki so uspešno izvoženi</string>\n    <string name=\"backup_export_failed\">Ne more pisati v datoteko varnostne kopije</string>\n    <string name=\"sd_card\">SD kartica</string>\n    <string name=\"sd_card_sync\">SD kartico Sinhroniziranje</string>\n    <string name=\"sd_card_summary\">Naloge se zrcalijo na SD kartico. Spremembe se sinhronizirajo obeh načinov. Upoštevajte, da bo izbris pila izbrisati opravila v uporabi!</string>\n    <string name=\"directory\">Imenik</string>\n    <string name=\"cannot_write_to_directory\">Ne more pisati v imenik</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sv/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Anteckningar</string>\n    <string name=\"title_create\">Ny anteckning</string>\n    <string name=\"timemachine\">Tidsmaskin</string>\n    <string name=\"time\">Tid</string>\n    <string name=\"done\">Färdig</string>\n    <string name=\"saved\">Sparad</string>\n    <string name=\"menu_save_and_add\">Spara och öppna ny</string>\n    <string name=\"menu_delete\">Ta bort</string>\n    <string name=\"menu_deletelist\">Ta bort listan</string>\n    <string name=\"menu_sync\">Synka</string>\n    <string name=\"menu_share\">Dela</string>\n    <string name=\"menu_preferences\">Inställningar</string>\n    <string name=\"menu_createlist\">Skapa lista</string>\n    <string name=\"menu_clearcompleted\">Ta bort slutförda</string>\n    <string name=\"menu_setdefaultlist\">Sätt som standardlista</string>\n    <string name=\"new_default_set\">Nya inställningar sparade</string>\n    <string name=\"menu_managelists\">Editera lista</string>\n    <string name=\"add_item\">Ny</string>\n    <string name=\"delete_question\">Ta bort?</string>\n    <string name=\"delete_items_message\">Är du säker att du vill ta bort dessa?</string>\n    <string name=\"delete_item_message\">Är du säker att du vill ta bort denna?</string>\n    <string name=\"delete_list_message\">Är du säker att du vill ta bort listan?</string>\n    <string name=\"editor_due_date_hint\">Förfallodatum</string>\n    <string name=\"editor_title_hint\">Titel</string>\n    <string name=\"editor_note_hint\">Anteckning</string>\n    <string name=\"resolve_edit\">Ändra</string>\n    <string name=\"default_style\">Standardstil</string>\n    <string name=\"show_items_as_tasks\">Visa som uppgifter</string>\n    <string name=\"show_items_as_notes\">Visa som anteckningar</string>\n    <string name=\"sort_list_default\">Standard sortering</string>\n    <string name=\"sort_list_alphabetical\">Sortera alfabetiskt</string>\n    <string name=\"sort_list_due\">Sortera efter datum</string>\n    <string name=\"sort_list_updated\">Sortera efter senast ändrad </string>\n    <string name=\"sort_list_manual\">Sortera manuellt</string>\n    <string name=\"search_description\">Anteckningar och uppgifter</string>\n    <string name=\"archive\">Arkiv</string>\n    <string name=\"restore\">Återställ</string>\n    <string name=\"restore_to\">Återställ till</string>\n    <string name=\"search_hint\">Sök</string>\n    <string name=\"settings_theme\">Nuvarande tema</string>\n    <string name=\"settings_theme_dialog\">Använd tema</string>\n    <string name=\"settings_lang\">Språk</string>\n    <string name=\"settings_summary_theme_black\">Svart</string>\n    <string name=\"settings_summary_theme_dark\">Mörk</string>\n    <string name=\"settings_summary_theme_light\">Ljus</string>\n    <string name=\"settings_summary_theme_classic\">Klassisk</string>\n    <string name=\"settings_cat_appearance\">Utseende</string>\n    <string name=\"preference_preview_text\">Så här kommer texten att se ut</string>\n    <string name=\"settings_account_title\">Välj ett konto</string>\n    <string name=\"settings_account_summary\">Tryck för att ändra konto</string>\n    <string name=\"settings_cat_syncing\">Synkronisering</string>\n    <string name=\"show_from_all_lists\">Alla listor</string>\n    <string name=\"lists\">Listor</string>\n    <string name=\"deleted\">Tog bort</string>\n    <string name=\"repeat\">Upprepa</string>\n    <string name=\"once\">En gång</string>\n    <string name=\"always\">Alltid</string>\n    <string name=\"permission_read_label\">läsa anteckningar och listor</string>\n    <string name=\"permission_read_desc\">Tillåter appen att läsa dina anteckningar och listor</string>\n    <string name=\"permission_write_label\">modifiera anteckningar och listor</string>\n    <string name=\"permission_write_desc\">Tillåter programmet att lägga till, ändra och ta bort anteckningar och listor</string>\n    <string name=\"settings_list_dialog\">Välj en lista</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"drag_to_timetravel\">Dra för att se tidigare versioner</string>\n    <string name=\"import_data_question\">Importera data?</string>\n    <string name=\"import_started\">Importerar data…</string>\n    <string name=\"imported_result\">Importerade %1$d anteckningar i %2$d listor</string>\n    <string name=\"import_error\">Något gick fel: %s</string>\n    <string name=\"move\">Flytta</string>\n    <string name=\"move_to\">Flytta till</string>\n    <string name=\"moved_x_to_list\">Flyttade %1$d till %2$s</string>\n    <string name=\"lock_note\">Lås anteckning</string>\n    <string name=\"unlock_note\">Lås upp anteckning</string>\n    <string name=\"locked\">Låst</string>\n    <string name=\"unlocked\">Upplåst</string>\n    <string name=\"password_required\">Lösenord krävs</string>\n    <string name=\"select_account\">Välj ett konto</string>\n    <string name=\"password\">Lösenord</string>\n    <string name=\"password_set\">Lösenord sparat</string>\n    <string name=\"password_cleared\">Lösenord togs bort</string>\n    <string name=\"passwords_dont_match\">Lösenorden är inte lika</string>\n    <string name=\"password_incorrect\">Felaktigt lösenord</string>\n    <string name=\"enter_password\">Skriv lösenord</string>\n    <string name=\"enter_new_password\">Nytt lösenord</string>\n    <string name=\"confirm_new_password\">Bekräfta nytt lösenord</string>\n    <string name=\"apply\">Ändra lösenord</string>\n    <string name=\"clear_password\">Ta bort lösenord</string>\n    <string name=\"password_info\">Genom att låsa en anteckning begränsas möjligheten att läsa och ändra den. Du låser anteckningar på individuall basis. Anteckningarna skyddas endast lokalt och är fullt åtkomstbara på andra enheter.</string>\n    <string name=\"about\">Om</string>\n    <string name=\"sync_failed\">Synk misslyckades</string>\n    <string name=\"sync_login_failed\">Inloggning misslyckades, kunde inte komma åt Google Tasks</string>\n    <string name=\"completed\">Slutförd</string>\n    <string name=\"date_header_overdue\">Försenade</string>\n    <string name=\"date_header_today\">Idag</string>\n    <string name=\"date_header_tomorrow\">Imorgon</string>\n    <string name=\"date_header_future\">Senare</string>\n    <string name=\"date_header_none\">Inget datum</string>\n    <string name=\"date_header_completed\">Avklarade</string>\n    <string name=\"next_5_days\">Kommande 5 dagarna</string>\n    <string name=\"next_n_days\">Kommande %d dagarna</string>\n    <string name=\"this_week\">Denna veckan</string>\n    <string name=\"please_select_note\">Välj eller skapa en anteckning</string>\n    <string name=\"please_create_note\">Skapa en anteckning först</string>\n    <string name=\"hide_checkbox\">Dölj kryssruta</string>\n    <string name=\"hide_checkbox_summary_on\">Kryssruta kommer att vara dold</string>\n    <string name=\"hide_checkbox_summary_off\">Kryssruta kommer att vara synlig</string>\n    <string name=\"hide_date\">Göm datum</string>\n    <string name=\"item_max_height\">Max höjd på anteckningar</string>\n    <string name=\"long_date_format\">Långt datumformat</string>\n    <string name=\"short_date_format\">Kort datumformat</string>\n    <string name=\"select_date\">Välj datum</string>\n    <string name=\"localedefault\">Språkspecifikt</string>\n    <string name=\"hide_header\">Dölj titel</string>\n    <string name=\"transparency\">Transparens</string>\n    <string name=\"notes_shortcut\">Anteckningsgenväg</string>\n    <string name=\"shortcut_help1\">Om vald, så kommer genvägen att skapa en ny anteckning i den valda listan. Annars öppnas bara den valda listan.</string>\n    <string name=\"loading_widget\">Laddar widget…</string>\n    <string name=\"default_list\">Standardlista</string>\n    <string name=\"please_type_before_reminder\">Skriv en anteckning innan du sätter en påminnelse</string>\n    <string name=\"notecopied_one\">Kopierade 1 anteckning</string>\n    <string name=\"notecopied_other\">Kopierade %d anteckningar</string>\n    <string name=\"notedeleted_one\">Raderade 1 anteckning</string>\n    <string name=\"notedeleted_other\">Raderade %d anteckningar</string>\n    <string name=\"selected_one\">Markerade 1 anteckning</string>\n    <string name=\"selected_other\">Markerade %d anteckningar</string>\n    <string name=\"background_sync\">Synka i bakgrunden</string>\n    <string name=\"background_sync_info\">Synka en gång per timme</string>\n    <string name=\"sync_on_change\">Synka vid ändringar</string>\n    <string name=\"sync_on_change_info\">Schemalägger en synk en kort tid efter att en anteckningar har ändrats</string>\n    <string name=\"sync_on_start\">Synka när appen startar</string>\n    <string name=\"sync_on_start_info\">Synkar som mest var femte minut</string>\n    <string name=\"snooze\">Snooza</string>\n    <string name=\"silent\">Tyst</string>\n    <string name=\"sound\">Ljud</string>\n    <string name=\"vibrate\">Vibrera</string>\n    <string name=\"reminders\">Påminnelser</string>\n    <string name=\"standard\">Standard</string>\n    <string name=\"notification_prio_high\">Hög: Högst upp, expanderad</string>\n    <string name=\"notification_prio_low\">Låg: Gömd, minimerad</string>\n    <string name=\"priority\">Prioritet</string>\n    <string name=\"notifications\">Påminnelser</string>\n    <string name=\"tasks\">Uppgifter</string>\n    <string name=\"all_tasks\">Alla uppgifter</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Endast uppgifter i</string>\n    <string name=\"dashclock_show_overdue_tasks\">Visa försenade uppgifter</string>\n    <string name=\"dashclock_overdue_tasks_show\">Försenade uppgifter visas</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Försenade uppgifter döljs</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense Notes inställningar</string>\n    <string name=\"dashclock_pref_header_general\">Allmänt</string>\n    <string name=\"dashclock_due_upper_limit_title\">Endast uppgifter som förfaller senast</string>\n    <string name=\"dashclock_today\">Idag</string>\n    <string name=\"dashclock_tomorrow\">Imorgon</string>\n    <string name=\"dashclock_next7\">Kommande 7 dagarna</string>\n    <string name=\"dashclock_anytime\">När som helst</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Visar så många som möjligt</string>\n    <string name=\"dashclock_show_only_the_next_task\">Visar endast nästa uppgift</string>\n    <string name=\"dashclock_header_shown\">Listans namn visas i fetstil</string>\n    <string name=\"dashclock_first_task_shown\">Den första uppgiftens titel visas i fetstil</string>\n    <string name=\"dashclock_display_header\">Visa listans namn</string>\n    <string name=\"editor\">Redigerare</string>\n    <string name=\"bold\">Fetstil</string>\n    <string name=\"italic\">Kursiv</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"title_style\">Titelstil</string>\n    <string name=\"title_font\">Titeltypsnitt</string>\n    <string name=\"body_font\">Anteckningens typsnitt</string>\n    <string name=\"clickable_links\">Klickbara länkar</string>\n    <string name=\"small\">Litet</string>\n    <string name=\"medium\">Medium</string>\n    <string name=\"large\">Stort</string>\n    <string name=\"text_size\">Textstorlek</string>\n    <string name=\"text\">Text</string>\n    <string name=\"add_a_reminder\">Lägg till påminnelse</string>\n    <string name=\"backup\">Säkerhetskopiera</string>\n    <string name=\"backup_import\">Importera backup</string>\n    <string name=\"backup_export\">Gör backup</string>\n    <string name=\"backup_import_msg\">Försök att importera backup från %1$s? Detta kommer radera den nuvarande databasen.</string>\n    <string name=\"backup_export_msg\">Exportera alla anteckningar till %1$s?</string>\n    <string name=\"backup_import_success\">Backup importerad</string>\n    <string name=\"backup_file_not_found\">Backupfilen kunde inte hittas</string>\n    <string name=\"backup_import_failed\">Misslyckades med att läsa backupfilen</string>\n    <string name=\"backup_export_success\">Backup skriven</string>\n    <string name=\"backup_export_failed\">Kan inte skriva till backupfilen</string>\n    <string name=\"sd_card\">SD-kort</string>\n    <string name=\"sd_card_sync\">SD-synkronisering</string>\n    <string name=\"sd_card_summary\">Anteckningar kopieras till SD-kortet. Ändringar synkroniseras båda till och från SD-kortet. Notera att filer som raderas från SD-kortet även raderar listorna i appen!</string>\n    <string name=\"directory\">Mapp</string>\n    <string name=\"cannot_write_to_directory\">Kan inte skriva till mapp</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw600dp/bool.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<!-- Scrollview in editor -->\n\t<bool name=\"fillEditor\">false</bool>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw600dp/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n\n<resources>\n\t<!-- List stuff -->\n\t<dimen name=\"listMargins\">16dp</dimen>\n\t<dimen name=\"listMarginsPadded\">@dimen/listMargins</dimen>\n\n\t<!-- Used in search where no note fragment exists ever -->\n\n\t<!-- Editor fragment stuff -->\n\t<dimen name=\"editor_side_margin\">16dp</dimen>\n\t<dimen name=\"editor_vertical_margin\">32dp</dimen>\n\t<dimen name=\"editor_vertical_margin_bottom\">16dp</dimen>\n\n\t<!-- For card in editor -->\n\n\t<dimen name=\"activity_lone_horizontal_margin\">100dp</dimen>\n\n\t<!-- WidgetConfig -->\n\t<dimen name=\"widget_conf_preview_land_width\">500dp</dimen>\n\t<dimen name=\"widget_conf_preview_port_height\">300dp</dimen>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw600dp-land/dimens.xml",
    "content": "<resources>\n\n\t<!--\n\t\t Customize dimensions originally defined in res/values/dimens.xml (such as\n\t\t screen margins) for sw600dp devices (e.g. 7\" tablets) in landscape here.\n\t-->\n\t<dimen name=\"activity_lone_horizontal_margin\">200dp</dimen>\n\t<dimen name=\"activity_lone_horizontal_padding\">64dp</dimen>\n\t<dimen name=\"listMargins\">0dp</dimen>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw600dp-land/layout_constants.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<!-- This is the density where Nexus7 will fall with 600x960 dp -->\n<resources>\n\n\t<integer name=\"leftFragmentWeight\">2</integer>\n\t<integer name=\"rightFragmentWeight\">3</integer>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw720dp/dimens.xml",
    "content": "<resources>\n\n\t<!--\n\t\t Customize dimensions originally defined in res/values/dimens.xml (such as\n\t\t screen margins) for sw720dp devices (e.g. 10\" tablets) in landscape here.\n\t-->\n\t<!-- Editor fragment stuff -->\n\t<dimen name=\"editor_side_margin\">32dp</dimen>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw720dp-land/dimens.xml",
    "content": "<resources>\n\n\t<!--\n\t\t Customize dimensions originally defined in res/values/dimens.xml (such as\n\t\t screen margins) for sw720dp devices (e.g. 10\" tablets) in landscape here.\n\t-->\n\t<dimen name=\"activity_lone_horizontal_margin\">250dp</dimen>\n\t<dimen name=\"activity_lone_horizontal_padding\">128dp</dimen>\n\t<dimen name=\"preference_screen_side_margin\">64dp</dimen>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw720dp-land/layout_constants.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<resources>\n\n\t<integer name=\"leftFragmentWeight\">3</integer>\n\t<integer name=\"rightFragmentWeight\">7</integer>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ta/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name_short\">குறிப்புகள்</string>\n    <string name=\"title_create\">புதிய குறிப்பு</string>\n    <string name=\"timemachine\">நேர இயந்திரம்</string>\n    <string name=\"time\">நேரம்</string>\n    <string name=\"done\">முடிந்தது</string>\n    <string name=\"saved\">சேமிக்கப்பட்டது</string>\n    <string name=\"menu_save_and_add\">சேமித்து குறிப்பைச் சேர்க்கவும்</string>\n    <string name=\"menu_delete\">நீக்கு</string>\n    <string name=\"menu_deletelist\">பட்டியலை நீக்கு</string>\n    <string name=\"menu_sync\">ஒத்திசைவு</string>\n    <string name=\"menu_share\">பங்கு</string>\n    <string name=\"menu_preferences\">அமைப்புகள்</string>\n    <string name=\"menu_createlist\">புதிய பட்டியலைத் தொடங்கவும்</string>\n    <string name=\"menu_clearcompleted\">தெளிவாக முடிந்தது</string>\n    <string name=\"menu_setdefaultlist\">இயல்புநிலை பட்டியலாக அமைக்கவும்</string>\n    <string name=\"new_default_set\">புதிய இயல்புநிலை தொகுப்பு</string>\n    <string name=\"menu_managelists\">பட்டியல்களைத் திருத்து</string>\n    <string name=\"add_item\">உருப்படியைச் சேர்க்கவும்</string>\n    <string name=\"delete_question\">நீக்கவா?</string>\n    <string name=\"delete_items_message\">இந்த உருப்படிகளை நீக்கவா?</string>\n    <string name=\"delete_item_message\">இந்த உருப்படியை நீக்கவா?</string>\n    <string name=\"delete_list_message\">இந்த பட்டியலை நீக்கவா?</string>\n    <string name=\"delete_completed_tasks_question\">நிறைவு செய்யப்பட்ட அனைத்து பணிகளையும் நீக்கவா?</string>\n    <string name=\"editor_due_date_hint\">இரண்டு தேதிகள்</string>\n    <string name=\"editor_title_hint\">தலைப்பு</string>\n    <string name=\"editor_note_hint\">குறிப்பு</string>\n    <string name=\"resolve_edit\">குறிப்பைத் திருத்து</string>\n    <string name=\"default_style\">இயல்புநிலை நடை</string>\n    <string name=\"show_items_as_tasks\">சரிபார்க்கக்கூடிய பணிகள்</string>\n    <string name=\"show_items_as_notes\">எளிய குறிப்புகள்</string>\n    <string name=\"sort_list_default\">இயல்புநிலை வரிசையாக்க வரிசை</string>\n    <string name=\"sort_list_due\">இரண்டு தேதிகள்</string>\n    <string name=\"sort_list_updated\">அண்மைக் கால புதுப்பிக்கப்பட்டது</string>\n    <string name=\"sort_list_manual\">கையேடு</string>\n    <string name=\"search_description\">குறிப்புகள் மற்றும் பணிகள்</string>\n    <string name=\"archive\">காப்பகம்</string>\n    <string name=\"restore\">மீட்டமை</string>\n    <string name=\"restore_to\">இதற்கு மீட்டமை</string>\n    <string name=\"search_hint\">தேடல்</string>\n    <string name=\"settings_theme\">தற்போதைய கருப்பொருள்</string>\n    <string name=\"settings_theme_dialog\">கருப்பொருள் பயன்படுத்தவும்</string>\n    <string name=\"settings_lang\">மொழி</string>\n    <string name=\"settings_summary_theme_black\">கருப்பு</string>\n    <string name=\"settings_summary_theme_dark\">இருண்ட</string>\n    <string name=\"settings_summary_theme_light\">ஒளி</string>\n    <string name=\"settings_summary_theme_classic\">கிளாசிக்</string>\n    <string name=\"preference_preview_text\">ஆசிரியர் உரை இப்படித்தான் தோன்றும்</string>\n    <string name=\"settings_account_title\">ஒரு கணக்கைத் தேர்ந்தெடுக்கவும்</string>\n    <string name=\"settings_cat_appearance\">தோற்றம்</string>\n    <string name=\"settings_account_summary\">கணக்கை மாற்றத் தொடவும்</string>\n    <string name=\"settings_cat_syncing\">ஒத்திசைக்கிறது</string>\n    <string name=\"show_from_all_lists\">அனைத்து பட்டியல்களும்</string>\n    <string name=\"permission_read_desc\">உங்கள் குறிப்புகள் மற்றும் தொடர்புடைய பட்டியல்களை முட்டாள்தனமான குறிப்புகளில் படிக்க பயன்பாட்டை அனுமதிக்கிறது</string>\n    <string name=\"lists\">பட்டியல்கள்</string>\n    <string name=\"permission_write_desc\">முட்டாள்தனமான குறிப்புகள் இல்லாத குறிப்புகள் மற்றும் பட்டியல்களை செருகவும், புதுப்பிக்கவும், நீக்கவும் பயன்பாட்டை அனுமதிக்கிறது</string>\n    <string name=\"navigation_drawer_close\">வழிசெலுத்தல் அலமாரியை மூடு</string>\n    <string name=\"import_data_question\">தரவை இறக்குமதி செய்யவா?</string>\n    <string name=\"import_started\">தரவை இறக்குமதி செய்தல்…</string>\n    <string name=\"imported_result\">இறக்குமதி செய்யப்பட்ட %1$d குறிப்புகள் %2$d பட்டியல்களில்</string>\n    <string name=\"notification_channel_name\">குறிப்புகளுக்கான நினைவூட்டல்கள்</string>\n    <string name=\"notification_channel_description\">தேர்ந்தெடுக்கப்பட்ட நினைவூட்டலின் போது குறிப்பு உள்ளடக்கத்தைக் காட்டுகிறது</string>\n    <string name=\"import_error\">ஏதோ தவறு நடந்தது: %s</string>\n    <string name=\"feature_is_WIP\">இந்த நற்பொருத்தம் ஒரு W.I.P.</string>\n    <string name=\"permission_denied\">தொடர முடியாது: நீங்கள் இசைவு மறுத்தீர்கள்</string>\n    <string name=\"no_sync_method_chosen\">நீங்கள் ஒரு ஒத்திசைவு முறையைத் தேர்வு செய்யவில்லை</string>\n    <string name=\"file_picker_not_available\">கோப்பு எடுப்பவர் கிடைக்கவில்லை</string>\n    <string name=\"choose_backup_folder\">காப்பு கோப்புறையைத் தேர்வுசெய்க</string>\n    <string name=\"use_exact_alarms\">சரியான அலாரங்களைப் பயன்படுத்துங்கள்</string>\n    <string name=\"exact_alarms_summary\">அறிவிப்பு நினைவூட்டல்களை மிகவும் நம்பகமான முறையில் காட்ட அதிக பேட்டரியைப் பயன்படுத்துகிறது</string>\n    <string name=\"disable_battery_optimizations\">பேட்டரி மேம்படுத்தல்களை அணைக்கவும்</string>\n    <string name=\"battery_optimizations_active\">பேட்டரி மேம்படுத்தல்கள்</string>\n    <string name=\"battery_optimizations_inactive\">பேட்டரி மேம்படுத்தல்கள் முடக்கப்படுகின்றன</string>\n    <string name=\"allow_exact_reminders\">சரியான நினைவூட்டல்களை அனுமதிக்கவும்</string>\n    <string name=\"allow_exact_reminders_summary\">இந்த பயன்பாட்டை நினைவூட்டல்களை மிகவும் நம்பகத்தன்மையுடன் அனுப்ப அனுமதிக்க ஒரு பக்கத்தைத் திறக்கவும். ஆண்ட்ராய்டு 12 ச்னோ கூம்பு முதல் மட்டுமே தேவை</string>\n    <string name=\"for_older_devices\">பழைய ஆண்ட்ராய்டு சாதனங்களுக்கு</string>\n    <string name=\"overwritten_in_newer_systems\">சில அறிவிப்பு சேனல் அமைப்புகளால் செயல்தவிர்க்கலாம்</string>\n    <string name=\"prefs_improved_reliability\">நம்பகத்தன்மையை மேம்படுத்துவதற்கான விருப்பத்தேர்வுகள்</string>\n    <string name=\"notification_channel_settings\">அறிவிப்பு சேனல் அமைப்புகள்</string>\n    <string name=\"open_channel_settings_description\">இந்த பயன்பாட்டிற்கான அறிவிப்பு அமைப்புகளைத் திருத்த ஒரு பக்கத்தைத் திறக்கவும். இவை வேறு இடங்களில் தேர்ந்தெடுக்கப்பட்ட எந்த அமைப்புகளையும் மீறுகின்றன</string>\n    <string name=\"libraries_used\">நூலகங்கள்</string>\n    <string name=\"app_about_dev_team\">சோனாச் கல்டர்ச்டாம் உருவாக்கியது மற்றும் காம்பெல்லோ மானுவல் பராமரித்தார்</string>\n    <string name=\"app_about_git_repo\">இந்த பயன்பாடு நகலெடுக்கப்பட்ட லிப்ரே மென்பொருளாகும், இது https://github.com/spacecowboy/notepad இல் கிடைக்கிறது</string>\n    <string name=\"app_about_license\">உரிமம் பெற்ற GPLV3+, https://www.gnu.org/licenses</string>\n    <string name=\"app_about_bugreports\">பிழைகள் மற்றும் சிக்கல்களை https://github.com/spacecowboy/notepad/issues இல் புகாரளிக்கவும்</string>\n    <string name=\"app_about_donations\">நாங்கள் நன்கொடைகளை ஏற்றுக்கொள்கிறோம். எங்கள் வேலையை ஆதரிக்க, %2$s இல் %1$s ஐத் தேடுங்கள்</string>\n    <string name=\"app_about_translations\">பயன்பாட்டை https://hosted.weblate.org/engage/no- nonsensens- நோட்ச்/இல் மொழிபெயர்க்க உதவுங்கள்</string>\n    <string name=\"disable_android_hibernation\">ஆண்ட்ராய்டு Hibernation ஐ முடக்கு</string>\n    <string name=\"disable_android_hibernation_desc\">இது பயன்பாட்டை நம்பகமானதாக மாற்றும் அம்சமாகும். இது உங்கள் நினைவூட்டல்களைத் தடுக்கலாம். இதைத் திறந்து, \\\"பயன்படுத்தப்படாவிட்டால் பயன்பாட்டு செயல்பாட்டை இடைநிறுத்துங்கள்\\\" போன்ற ஒன்றைத் தேடி அதை அணைக்கவும்</string>\n    <string name=\"msg_hibernation_already_off\">ஆண்ட்ராய்டு ஐபர்னேசன் ஏற்கனவே முடக்கப்பட்டுள்ளது, உங்களுக்கு இது தேவையில்லை</string>\n    <string name=\"changelog_online\">சேஞ்ச்லாக் (ஆன்லைன்)</string>\n    <string name=\"canceled_note_locked\">ரத்துசெய்யப்பட்டது: குறிப்பு பூட்டப்பட்டுள்ளது</string>\n    <string name=\"order_notes_by\">வழங்கியவர்:</string>\n    <string name=\"show_elements_as\">இவ்வாறு உருப்படிகளைக் காட்டு:</string>\n    <string name=\"version\">பதிப்பு: %s</string>\n    <string name=\"next_month\">அடுத்த மாதம்</string>\n    <string name=\"next_year\">அடுத்த ஆண்டு</string>\n    <string name=\"select_all\">அனைத்தையும் தெரிவுசெய்</string>\n    <string name=\"tutorial_online\">பயிற்சி (ஆன்லைன்)</string>\n    <string name=\"showcase_tutorial_title\">இழந்ததாக உணர்கிறீர்களா?</string>\n    <string name=\"showcase_tutorial_description\">எங்களிடம் நிகழ்நிலை பயிற்சி உள்ளது. அமைப்புகளில் அதைக் கண்டறியவும்</string>\n    <string name=\"welcome_note_title\">வரவேற்கிறோம்!</string>\n    <string name=\"welcome_note_row_2\">தொடங்க இதைத் திறக்கவும்.</string>\n    <string name=\"welcome_note_row_3\">இந்த பயன்பாட்டில் விரிவான பயிற்சி உள்ளது. நீங்கள் அதை காணலாம்</string>\n    <string name=\"unsupported_readonly_file\">ஆதரிக்கப்படாத வாசிப்பு மட்டும் கோப்பு: %s</string>\n    <string name=\"show_completed_notes\">நிறைவு செய்யப்பட்ட குறிப்புகளைக் காட்டு</string>\n    <string name=\"move\">நகர்த்தவும்</string>\n    <string name=\"move_to\">செல்லுங்கள்</string>\n    <string name=\"lock_note\">பூட்டு குறிப்பு</string>\n    <string name=\"moved_x_to_list\">%1$d க்கு %2$s நகர்த்தப்பட்டது</string>\n    <string name=\"account\">கணக்கு</string>\n    <string name=\"unlock_note\">குறிப்பைத் திறக்கவும்</string>\n    <string name=\"locked\">பூட்டப்பட்டுள்ளது</string>\n    <string name=\"unlocked\">திறக்கப்பட்டது</string>\n    <string name=\"password_required\">கடவுச்சொல் தேவை</string>\n    <string name=\"select_account\">ஒரு கணக்கைத் தேர்ந்தெடுக்கவும்</string>\n    <string name=\"password\">கடவுச்சொல்</string>\n    <string name=\"password_set\">கடவுச்சொல் தொகுப்பு</string>\n    <string name=\"password_cleared\">கடவுச்சொல் அழிக்கப்பட்டது</string>\n    <string name=\"enter_password\">கடவுச்சொல்லை உள்ளிடவும்</string>\n    <string name=\"passwords_dont_match\">கடவுச்சொற்கள் பொருந்தவில்லை</string>\n    <string name=\"password_incorrect\">கடவுச்சொல் தவறானது</string>\n    <string name=\"enter_new_password\">புதிய கடவுச்சொல்லை உள்ளிடவும்</string>\n    <string name=\"confirm_new_password\">புதிய கடவுச்சொல்லை உறுதிப்படுத்தவும்</string>\n    <string name=\"apply\">இடு</string>\n    <string name=\"clear_password\">கடவுச்சொல்லை அழிக்கவும்</string>\n    <string name=\"password_info\">எந்தவொரு குறிப்பையும் பூட்டலாம், அதைப் பார்க்கவும் மாற்றியமைக்கவும் முடியும். இது மற்ற சாதனங்களில் முழுமையாக அணுகக்கூடியதாக உள்ளது.</string>\n    <string name=\"about\">பற்றி</string>\n    <string name=\"sync_failed\">ஒத்திசைவு தோல்வியுற்றது</string>\n    <string name=\"sync_login_failed\">Google பணிகளைப் பயன்படுத்த உள்நுழைய முடியவில்லை</string>\n    <string name=\"completed\">முடிந்தது</string>\n    <string name=\"date_header_overdue\">தாமதமானது</string>\n    <string name=\"date_header_today\">இன்று</string>\n    <string name=\"date_header_future\">பின்னர்</string>\n    <string name=\"date_header_tomorrow\">நாளை</string>\n    <string name=\"date_header_none\">தேதி இல்லை</string>\n    <string name=\"date_header_completed\">முடிந்தது</string>\n    <string name=\"next_5_days\">அடுத்த 5 நாட்கள்</string>\n    <string name=\"next_n_days\">அடுத்த %d நாட்கள்</string>\n    <string name=\"this_week\">இந்த வாரம்</string>\n    <string name=\"please_select_note\">தயவுசெய்து ஒரு குறிப்பைத் தேர்ந்தெடுக்கவும் அல்லது உருவாக்கவும்</string>\n    <string name=\"please_create_note\">குறிப்பை உருவாக்கவும்</string>\n    <string name=\"hide_checkbox\">தேர்வுப்பெட்டியை மறைக்கவும்</string>\n    <string name=\"hide_checkbox_summary_on\">தேர்வுப்பெட்டி மறைக்கப்பட்டுள்ளது</string>\n    <string name=\"hide_checkbox_summary_off\">தேர்வுப்பெட்டி காட்டப்பட்டுள்ளது</string>\n    <string name=\"hide_date\">உரிய தேதியை மறைக்கவும்</string>\n    <string name=\"item_max_height\">குறிப்புகள்/பணிகளின் அதிகபட்ச உயரம், வரிசைகளில்</string>\n    <string name=\"long_date_format\">பேனலுக்கு நீண்ட கால வடிவமைப்பு</string>\n    <string name=\"short_date_format\">குறுகிய தேதி வடிவம், பட்டியலுக்கு</string>\n    <string name=\"select_date\">தேதியைத் தேர்ந்தெடுக்கவும்</string>\n    <string name=\"localedefault\">சாதன இயல்புநிலை</string>\n    <string name=\"hide_header\">முழு விட்செட் தலைப்பையும் மறைக்கவும்</string>\n    <string name=\"transparency\">வெளிப்படைத்தன்மை</string>\n    <string name=\"notes_shortcut\">குறிப்புகள் குறுக்குவழி</string>\n    <string name=\"shortcut_help1\">சார்ட்கட் திறந்த உரை எடிட்டருடன் தேர்ந்தெடுக்கப்பட்ட பட்டியலில் புதிய குறிப்பைத் திறக்கச் செய்கிறது.</string>\n    <string name=\"loading_widget\">விட்செட்டை ஏற்றுகிறது…</string>\n    <string name=\"default_list\">இயல்புநிலை பட்டியல்</string>\n    <string name=\"please_type_before_reminder\">நினைவூட்டலைச் சேர்ப்பதற்கு முன் சில உரையைத் தட்டச்சு செய்க</string>\n    <string name=\"notecopied_one\">ஒரு குறிப்பை நகலெடுத்தார்</string>\n    <string name=\"notecopied_other\">%d குறிப்புகள் நகலெடுக்கப்பட்டன</string>\n    <string name=\"notedeleted_one\">ஒனனோட்டை நீக்கியது</string>\n    <string name=\"notedeleted_other\">நீக்கப்பட்டது %d குறிப்புகள்</string>\n    <string name=\"selected_one\">ஒரு குறிப்பைத் தேர்ந்தெடுத்தது</string>\n    <string name=\"selected_other\">தேர்ந்தெடுக்கப்பட்ட %d குறிப்புகள்</string>\n    <string name=\"background_sync\">பின்னணி ஒத்திசைவு</string>\n    <string name=\"background_sync_info\">ஒரு மணி நேரத்திற்கு ஒரு முறை ஒத்திசைக்கிறது</string>\n    <string name=\"sync_on_change\">மாற்றங்களில் ஒத்திசைக்கவும்</string>\n    <string name=\"sync_on_change_info\">குறிப்பு மாறிய சிறிது நேரத்திலேயே ஒரு ஒத்திசைவை திட்டமிடுகிறது</string>\n    <string name=\"sync_on_start\">பயன்பாட்டு தொடக்கத்தில் ஒத்திசைக்கவும்</string>\n    <string name=\"sync_on_start_info\">ஒவ்வொரு 5 நிமிடங்களுக்கும் ஒரு முறை ஒத்திசைக்கும் வரம்புகள்</string>\n    <string name=\"snooze\">உறக்கநிலை</string>\n    <string name=\"silent\">அமைதியாக</string>\n    <string name=\"sound\">ஒலி</string>\n    <string name=\"vibrate\">அதிர்வு</string>\n    <string name=\"reminders\">நினைவூட்டல்கள்</string>\n    <string name=\"standard\">தரநிலை</string>\n    <string name=\"notification_prio_high\">உயர்: மேலே, விரிவாக்கப்பட்டது</string>\n    <string name=\"notification_prio_low\">குறைந்த: மறைக்கப்பட்ட, குறைக்கப்பட்ட</string>\n    <string name=\"priority\">முன்னுரிமை</string>\n    <string name=\"notifications\">அறிவிப்புகள்</string>\n    <string name=\"tasks\">பணிகள்</string>\n    <string name=\"all_tasks\">அனைத்து பணிகளும்</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">பணிகளை கட்டுப்படுத்துங்கள்</string>\n    <string name=\"dashclock_show_overdue_tasks\">தாமதமான பணிகளைக் காட்டு</string>\n    <string name=\"dashclock_overdue_tasks_show\">தாமதமான பணிகள் காட்டப்பட்டுள்ளன</string>\n    <string name=\"dashclock_overdue_tasks_hide\">தாமதமான பணிகள் மறைக்கப்பட்டுள்ளன</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">அமைப்புகள்</string>\n    <string name=\"dashclock_pref_header_general\">பொது</string>\n    <string name=\"dashclock_due_upper_limit_title\">அண்மைக் கால பணிகளுக்கு வரம்பு</string>\n    <string name=\"dashclock_today\">இன்று</string>\n    <string name=\"dashclock_tomorrow\">நாளை</string>\n    <string name=\"dashclock_next7\">அடுத்த 7 நாட்கள்</string>\n    <string name=\"dashclock_anytime\">எப்போது வேண்டுமானாலும்</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">முடிந்தவரை பலவற்றைக் காட்டுங்கள்</string>\n    <string name=\"dashclock_show_only_the_next_task\">அடுத்த பணியை மட்டும் காட்டு</string>\n    <string name=\"dashclock_header_shown\">தைரியமான பட்டியல் பெயர்</string>\n    <string name=\"dashclock_first_task_shown\">முதல் பணியின் தைரியமான தலைப்பு</string>\n    <string name=\"dashclock_display_header\">காட்சி பட்டியல் பெயர்</string>\n    <string name=\"editor\">திருத்தி</string>\n    <string name=\"bold\">தடிமான</string>\n    <string name=\"clickable_links\">சொடுக்கு செய்யக்கூடிய இணைப்புகள்</string>\n    <string name=\"small\">சிறிய</string>\n    <string name=\"medium\">சராசரி</string>\n    <string name=\"large\">பெரிய</string>\n    <string name=\"text_size\">உரை அளவு</string>\n    <string name=\"text\">உரை</string>\n    <string name=\"add_a_reminder\">ஒரு நினைவூட்டலைச் சேர்க்கவும்</string>\n    <string name=\"backup\">காப்புப்பிரதி</string>\n    <string name=\"backup_import\">காப்புப்பிரதி இறக்குமதி செய்யுங்கள்</string>\n    <string name=\"backup_export_msg\">அனைத்து குறிப்புகளையும் %1$s க்கு ஏற்றுமதி செய்யவா?</string>\n    <string name=\"backup_import_success\">காப்புப்பிரதி இறக்குமதி செய்யப்பட்டது</string>\n    <string name=\"backup_import_failed\">காப்புப்பிரதி கோப்பை nononsenonotes_backup.json ஐப் படிக்க முடியவில்லை</string>\n    <string name=\"backup_export_success\">காப்புப்பிரதி ஏற்றுமதி செய்யப்பட்டது</string>\n    <string name=\"backup_export_failed\">காப்பு கோப்பில் எழுத முடியவில்லை</string>\n    <string name=\"backup_file_not_found\">Nononsensenotes_backup.json என்ற காப்புப் கோப்பைக் கண்டுபிடிக்க முடியவில்லை</string>\n    <string name=\"sd_card\">எச்டி அட்டை</string>\n    <string name=\"sd_card_sync\">எச்டி அட்டை ஒத்திசைவு</string>\n    <string name=\"directory_summary_msg\">கோப்புகள் %s இல் சேமிக்கப்படுகின்றன\\n உங்கள் கோப்பு மேலாளர் பயன்பாட்டுடன் நீங்கள் அணுகலாம். பயன்பாட்டை நிறுவல் நீக்குவதும் இந்த கோப்புகளையும் நீக்கிவிடும்: உங்கள் குறிப்புகளை வைத்திருக்க, காப்புப்பிரதி செய்யுங்கள்</string>\n    <string name=\"bigger_titles\">பெரிய தலைப்புகள்</string>\n    <string name=\"bigger_titles_summary\">தலைப்பு முதல் வரி</string>\n    <string name=\"sorting\">வரிசைப்படுத்துதல்</string>\n    <string name=\"undo\">செயல்தவிர்</string>\n    <string name=\"sort_list_alphabetical\">A-z</string>\n    <string name=\"deleted\">நீக்கப்பட்டது</string>\n    <string name=\"repeat\">மீண்டும்</string>\n    <string name=\"once\">ஒருமுறை</string>\n    <string name=\"always\">எப்போதும்</string>\n    <string name=\"permission_read_label\">குறிப்புகள் மற்றும் பட்டியல்களைப் படியுங்கள்</string>\n    <string name=\"permission_write_label\">குறிப்புகள் மற்றும் பட்டியல்களை மாற்றவும்</string>\n    <string name=\"settings_list_dialog\">ஒரு பட்டியலைத் தேர்ந்தெடுக்கவும்</string>\n    <string name=\"settings_list\">பட்டியல்</string>\n    <string name=\"drag_to_timetravel\">நேர பயணத்திற்கு இழுக்கவும்</string>\n    <string name=\"navigation_drawer_open\">திறந்த வழிசெலுத்தல் அலமாரியை</string>\n    <string name=\"msg_enable_notifications\">அறிவிப்புகளை இயக்க அமைப்புகள் -&gt; அறிவிப்புகளுக்குச் செல்லவும்</string>\n    <string name=\"notifications_visibility\">அறிவிப்புகள் தெரிவுநிலை</string>\n    <string name=\"notifications_enabled\">அறிவிப்புகள் தெரியும்</string>\n    <string name=\"notifications_blocked\">அறிவிப்புகள் தெரியவில்லை, எனவே நீங்கள் நினைவூட்டல்களைக் காண மாட்டீர்கள். இங்கே தொடவும், இசைவு வகையைக் கண்டுபிடித்து, இந்த பயன்பாட்டிற்கான அறிவிப்புகளை இயக்கவும்</string>\n    <string name=\"unavailable_chose_directory\">கிடைக்கவில்லை. முதலில் ஒரு கோப்பகத்தைத் தேர்வுசெய்க</string>\n    <string name=\"not_selected_yet\">இன்னும் தேர்ந்தெடுக்கப்படவில்லை</string>\n    <string name=\"enable_sync\">ஒத்திசைவை இயக்கவும்</string>\n    <string name=\"enable_sync_desc\">ஒத்திசைவு அம்சங்களை செயல்படுத்துகிறது மற்றும் ஒத்திசைவு மூலத்தைத் தேர்வுசெய்ய அனுமதிக்கிறது. காப்புப்பிரதிகள் பாதிக்கப்படவில்லை</string>\n    <string name=\"italic\">சாய்வு</string>\n    <string name=\"normal\">சாதாரண</string>\n    <string name=\"title_style\">தலைப்பு நடை</string>\n    <string name=\"title_font\">தலைப்பு எழுத்துரு</string>\n    <string name=\"body_font\">உடல் எழுத்துரு</string>\n    <string name=\"backup_export\">காப்புப்பிரதி ஏற்றுமதி</string>\n    <string name=\"backup_import_msg\">%1$s இலிருந்து காப்புப்பிரதியை இறக்குமதி செய்ய முயற்சிக்கிறீர்களா? இது தற்போதைய தரவுத்தளத்தை அழிக்கும்.</string>\n    <string name=\"sd_card_summary\">பயன்பாட்டிற்கும் எச்டி கார்டுக்கும் இடையில் பணிகள் ஒரே மாதிரியாக வைக்கப்படுகின்றன. கோப்புகளை நீக்குவது இதனால் பயன்பாட்டில் உள்ள பணிகளை நீக்குகிறது!</string>\n    <string name=\"directory\">கோப்புறை</string>\n    <string name=\"cannot_write_to_directory\">கோப்பகத்திற்கு எழுத முடியாது</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Notlar</string>\n    <string name=\"title_create\">Yeni not</string>\n    <string name=\"timemachine\">Zaman makinesi</string>\n    <string name=\"time\">zaman</string>\n    <string name=\"done\">Tamam</string>\n    <string name=\"saved\">Kaydedildi</string>\n    <string name=\"menu_save_and_add\">not ekleyin ve Kaydedin</string>\n    <string name=\"menu_delete\">Sil</string>\n    <string name=\"menu_deletelist\">Listeyi sil</string>\n    <string name=\"menu_sync\">Yenile</string>\n    <string name=\"menu_share\">Paylaş</string>\n    <string name=\"menu_preferences\">Ayarlar</string>\n    <string name=\"menu_createlist\">Liste oluştur</string>\n    <string name=\"menu_clearcompleted\">Tamamlananları temizle</string>\n    <string name=\"menu_setdefaultlist\">Varsayılan liste olarak ayarla</string>\n    <string name=\"new_default_set\">Yeni varsayılan ayarlar</string>\n    <string name=\"menu_managelists\">Listeleri düzenle</string>\n    <string name=\"add_item\">Öğe ekle</string>\n    <string name=\"delete_question\">Sil?</string>\n    <string name=\"delete_items_message\">Bu ögeler silinsin mi?</string>\n    <string name=\"delete_item_message\">Bu öge silinsin mi?</string>\n    <string name=\"delete_list_message\">Bu liste silinsin mi?</string>\n    <string name=\"editor_due_date_hint\">Bitiş tarihi</string>\n    <string name=\"editor_title_hint\">Başlık</string>\n    <string name=\"editor_note_hint\">Not</string>\n    <string name=\"resolve_edit\">Notu düzenle</string>\n    <string name=\"default_style\">Varsayılan stil</string>\n    <string name=\"show_items_as_tasks\">işaretlenebilir görevler</string>\n    <string name=\"show_items_as_notes\">basit notlar</string>\n    <string name=\"sort_list_default\">Varsayılan sıralama türü</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_due\">Bitiş tarihi</string>\n    <string name=\"sort_list_updated\">En son güncellenen</string>\n    <string name=\"sort_list_manual\">Elle</string>\n    <string name=\"search_description\">Notlar ve görevler</string>\n    <string name=\"archive\">Arşiv</string>\n    <string name=\"restore\">Eski haline çevir</string>\n    <string name=\"restore_to\">Eski haline çevir</string>\n    <string name=\"search_hint\">Ara</string>\n    <string name=\"settings_theme\">Kullanılan tema</string>\n    <string name=\"settings_theme_dialog\">Tema seçin</string>\n    <string name=\"settings_lang\">Dil</string>\n    <string name=\"settings_summary_theme_black\">Siyah</string>\n    <string name=\"settings_summary_theme_dark\">Koyu</string>\n    <string name=\"settings_summary_theme_light\">Açık</string>\n    <string name=\"settings_summary_theme_classic\">Klasik</string>\n    <string name=\"settings_cat_appearance\">Görünüş</string>\n    <string name=\"preference_preview_text\">Düzenleyici, bu şekilde gösterilecektir</string>\n    <string name=\"settings_account_title\">Bir hesap seçin</string>\n    <string name=\"settings_account_summary\">Başka bir hesaba geçmek için dokunun</string>\n    <string name=\"settings_cat_syncing\">Eşitleme</string>\n    <string name=\"show_from_all_lists\">Tüm listeler</string>\n    <string name=\"lists\">Listeler</string>\n    <string name=\"deleted\">Silindi</string>\n    <string name=\"repeat\">Tekrarla</string>\n    <string name=\"once\">Bir defa</string>\n    <string name=\"always\">Herzaman</string>\n    <string name=\"permission_read_label\">Notları ve listeleri oku</string>\n    <string name=\"permission_read_desc\">Uygulamanın notlarınıza ve onların bağlı olduğu listelere erişmesine izin verir</string>\n    <string name=\"permission_write_label\">Notları ve listeleri değiştir</string>\n    <string name=\"permission_write_desc\">Uygulamanın yeni notlar ve listeler oluşturmasına, varolanları düzenlemesine veya silmesine izin verir</string>\n    <string name=\"settings_list_dialog\">Bir liste seçin</string>\n    <string name=\"settings_list\">Liste</string>\n    <string name=\"drag_to_timetravel\">Zamanda yolculuk sürükleyin</string>\n    <string name=\"import_data_question\">Veri aktarılsın mı?</string>\n    <string name=\"import_started\">Veriler aktarılıyor…</string>\n    <string name=\"imported_result\">%1$d not %2$d listeye aktarıldı</string>\n    <string name=\"import_error\">Bir şeyler yanlış gitti: %s</string>\n    <!--\n        Use few if language need it. In English is the same.\n        <item quantity=\"few\">Copied 2 notes.</item>\n    -->\n    <string name=\"move\">Taşı</string>\n    <string name=\"move_to\">Buraya taşı</string>\n    <string name=\"moved_x_to_list\">Buraya%1$d taşındı%2$s</string>\n    <string name=\"lock_note\">Notu kilitle</string>\n    <string name=\"unlock_note\">Kilidi kaldır</string>\n    <string name=\"locked\">Kilitlendi</string>\n    <string name=\"unlocked\">Kilit kaldırıldı</string>\n    <string name=\"password_required\">Parola gerekli</string>\n    <string name=\"select_account\">Bir hesap seçin</string>\n    <string name=\"password\">Parola</string>\n    <string name=\"password_set\">Parola belirlendi</string>\n    <string name=\"password_cleared\">Parola temizlendi</string>\n    <string name=\"passwords_dont_match\">Parolalar uyuşmuyor</string>\n    <string name=\"password_incorrect\">Parola yanlış</string>\n    <string name=\"enter_password\">Parolayı girin</string>\n    <string name=\"enter_new_password\">Yeni parolayı girin</string>\n    <string name=\"confirm_new_password\">Yeni parolayı doğrulayın</string>\n    <string name=\"apply\">Uygula</string>\n    <string name=\"clear_password\">Parolayı kaldır</string>\n    <string name=\"password_info\">Herhangi bir not kilitlenerek görüntülenmesi ve değiştirilmesi sınırlandırılabilir. Diğer aygıtlarda tamamen erişilebilir kalır.</string>\n    <string name=\"about\">Hakkında</string>\n    <string name=\"sync_failed\">Senkronizasyon başarısız</string>\n    <string name=\"sync_login_failed\">Google Görevler\\'i kullanmak için oturum açılamadı</string>\n    <string name=\"completed\">Tamamlanmış</string>\n    <string name=\"date_header_overdue\">Son gün</string>\n    <string name=\"date_header_today\">Bugün</string>\n    <string name=\"date_header_tomorrow\">Yarın</string>\n    <string name=\"date_header_future\">İlerki zaman</string>\n    <string name=\"date_header_none\">Tarih belirlenmemiş</string>\n    <string name=\"date_header_completed\">Tamamlanmış</string>\n    <string name=\"next_5_days\">Sonraki 5 gün</string>\n    <string name=\"next_n_days\">Sonraki %d günler</string>\n    <string name=\"this_week\">Bu hafta</string>\n    <string name=\"please_select_note\">Lütfen bir not seçin ya da oluşturun</string>\n    <string name=\"please_create_note\">Lütfen bir not oluşturun</string>\n    <string name=\"hide_checkbox\">İşaretleme kutusunu gizle</string>\n    <string name=\"hide_checkbox_summary_on\">İşaretleme kutusu gizli</string>\n    <string name=\"hide_checkbox_summary_off\">İşaretleme kutusu gösteriliyor</string>\n    <string name=\"hide_date\">Bitiş tarihini gizle</string>\n    <string name=\"item_max_height\">Maximum not uzunluğu/görevler</string>\n    <string name=\"long_date_format\">Panel için uzun tarih biçimi</string>\n    <string name=\"short_date_format\">Liste için kısa tarih biçimi</string>\n    <string name=\"select_date\">Tarih seç</string>\n    <string name=\"localedefault\">Varsayılan yerel ayar</string>\n    <!-- settings string keys -->\n    <string name=\"hide_header\">Başlık panelini gizle</string>\n    <string name=\"transparency\">Saydamlık</string>\n    <string name=\"notes_shortcut\">Kısayol notları</string>\n    <string name=\"shortcut_help1\">Kısayolun seçili listede açık bir metin düzenleyiciyle yeni bir not açmasını sağlar.</string>\n    <string name=\"loading_widget\">Widget yükleniyor…</string>\n    <string name=\"default_list\">Varsayılan liste</string>\n    <string name=\"please_type_before_reminder\">Lütfen bir hatırlatıcı ayarlamadan önce birkaç metin yazın</string>\n    <string name=\"notecopied_one\">1 not kopyalandı</string>\n    <string name=\"notecopied_other\">%d tane not kopyalandı</string>\n    <string name=\"notedeleted_one\">1 not silindi</string>\n    <string name=\"notedeleted_other\">%d tane not silindi</string>\n    <string name=\"selected_one\">1 not seçildi</string>\n    <string name=\"selected_other\">%d tane not seçildi</string>\n    <string name=\"background_sync\">Arkaplan senkronizasyonu</string>\n    <string name=\"background_sync_info\">Saatte bir defa senkronize et</string>\n    <string name=\"sync_on_change\">Değişiklikleri senkronize et</string>\n    <string name=\"sync_on_change_info\">Herhangi bir not değiştikten kısa bir süre sonra bir senkronizasyonu zamanlar</string>\n    <string name=\"sync_on_start\">Uygulamanın başlangıcında senkronize et</string>\n    <string name=\"sync_on_start_info\">Eşitlemeyi her 5 dakikada bir ile sınırlar</string>\n    <string name=\"snooze\">Ertele</string>\n    <string name=\"silent\">Sessize al</string>\n    <string name=\"sound\">Sesli</string>\n    <string name=\"vibrate\">Titreşim</string>\n    <string name=\"reminders\">Hatırlatmalar</string>\n    <string name=\"standard\">Standart</string>\n    <string name=\"notification_prio_high\">Yüksek: en iyiler , genişletildi</string>\n    <string name=\"notification_prio_low\">Düşük: Gizlendi, küçültüldü</string>\n    <string name=\"priority\">Öncelikli</string>\n    <string name=\"notifications\">Bildirimler</string>\n    <string name=\"tasks\">Görevler</string>\n    <string name=\"all_tasks\">Tüm görevler</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Görevleri sınırla</string>\n    <string name=\"dashclock_show_overdue_tasks\">Gecikmiş görevleri göster</string>\n    <string name=\"dashclock_overdue_tasks_show\">Gecikmiş görevler gösteriliyor</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Gecikmiş görevler gizli</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Ayarlar</string>\n    <string name=\"dashclock_pref_header_general\">Genel</string>\n    <string name=\"dashclock_due_upper_limit_title\">Görevleri en son sınırla</string>\n    <string name=\"dashclock_today\">Bugün</string>\n    <string name=\"dashclock_tomorrow\">Yarın</string>\n    <string name=\"dashclock_next7\">7 gün sonra</string>\n    <string name=\"dashclock_anytime\">Herzaman</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Mümkün olduğu kadar fazla göster</string>\n    <string name=\"dashclock_show_only_the_next_task\">Yalnızca bir sonraki görevi göster</string>\n    <string name=\"dashclock_header_shown\">Liste adı kalın</string>\n    <string name=\"dashclock_first_task_shown\">İlk görevin başlığı kalın</string>\n    <string name=\"dashclock_display_header\">Liste adını görüntüle</string>\n    <string name=\"editor\">Editör</string>\n    <string name=\"bold\">Kalın</string>\n    <string name=\"italic\">İtalik/Eğik Yazı</string>\n    <string name=\"normal\">Normal</string>\n    <string name=\"title_style\">Başlık stili</string>\n    <string name=\"title_font\">Başlık yazı tipi</string>\n    <string name=\"body_font\">Gövde yazı tipi</string>\n    <string name=\"clickable_links\">Tıklanabilir bağlantılar</string>\n    <string name=\"small\">Küçük</string>\n    <string name=\"medium\">Orta</string>\n    <string name=\"large\">Büyük</string>\n    <string name=\"text_size\">Metin boyutu</string>\n    <string name=\"text\">Metin</string>\n    <string name=\"add_a_reminder\">Hatırlatma ekle</string>\n    <string name=\"backup\">Yedekle</string>\n    <string name=\"backup_import\">Yedeği aktar</string>\n    <string name=\"backup_export\">Yedeği dışa aktar</string>\n    <string name=\"backup_import_msg\">Yedeklemeyi içe aktarın %1$s Bu, mevcut veri tabanını temizleyecektir.</string>\n    <string name=\"backup_export_msg\">Bütün notları dışa aktar%1$s?</string>\n    <string name=\"backup_import_success\">Yedekleme içe aktarıldı</string>\n    <string name=\"backup_file_not_found\">NoNonsenseNotes_Backup.json adında yedek dosyası bulunamadı</string>\n    <string name=\"backup_import_failed\">NoNonsenseNotes_Backup.json yedek dosyası okunamadı</string>\n    <string name=\"backup_export_success\">Yedek dışa aktarıldı</string>\n    <string name=\"backup_export_failed\">Yedek dosyasına yazılamıyor</string>\n    <string name=\"sd_card\">SD kart</string>\n    <string name=\"sd_card_sync\">SD kart eşitlemesi</string>\n    <string name=\"sd_card_summary\">Görevler uygulama ve SD kart arasında aynı tutulur. Bu nedenle dosyaların silinmesi uygulamadaki görevleri de siler!</string>\n    <string name=\"directory\">Klasör</string>\n    <string name=\"cannot_write_to_directory\">Rehbere yazılmıyor</string>\n    <string name=\"navigation_drawer_open\">Gezinme çekmecesini aç</string>\n    <string name=\"navigation_drawer_close\">Gezinme çekmecesini kapat</string>\n    <string name=\"feature_is_WIP\">Bu özelliğin üzerinde çalışılıyor.</string>\n    <string name=\"permission_denied\">Devam edilemiyor: izni reddettiniz</string>\n    <string name=\"no_sync_method_chosen\">Bir eşitleme yöntemi seçmediniz</string>\n    <string name=\"file_picker_not_available\">Dosya seçici kullanılamıyor</string>\n    <string name=\"choose_backup_folder\">Yedekleme klasörü seçin</string>\n    <string name=\"disable_battery_optimizations\">Pil iyileştirmelerini kapat</string>\n    <string name=\"battery_optimizations_active\">Pil iyileştirmeleri açık</string>\n    <string name=\"battery_optimizations_inactive\">Pil iyileştirmeleri kapalı</string>\n    <string name=\"libraries_used\">Kütüphaneler</string>\n    <string name=\"app_about_donations\">Bağış kabul ediyoruz. İşimizi deseklemek için, %2$s adresinde %1$s için arayın</string>\n    <string name=\"disable_android_hibernation\">Android hazırda bekletme modunu devre dışı bırak</string>\n    <string name=\"disable_android_hibernation_desc\">Uygulamayı daha az güvenilir kılan bir özelliktir. Hatırlatıcılarınızı engelleyebilir. Bunu açın, \\\"kullanılmıyorsa uygulama etkinliğini askıya al\\\" gibi bir şey arayın ve kapatın</string>\n    <string name=\"msg_hibernation_already_off\">Android hazırda bekletme modu zaten KAPALI, buna ihtiyacınız yok</string>\n    <string name=\"msg_enable_notifications\">Bildirimleri etkinleştirmek için ayarlar -&gt; bildirimler bölümüne gidin</string>\n    <string name=\"notifications_visibility\">Bildirim görünürlüğü</string>\n    <string name=\"notifications_enabled\">Bildirimler görünür</string>\n    <string name=\"enable_sync_desc\">Eşitleme özelliklerini etkinleştirir ve bir eşitleme kaynağı seçmeye izin verir. Yedeklemeler etkilenmez</string>\n    <string name=\"show_elements_as\">Ögeleri gösterme biçimi:</string>\n    <string name=\"version\">Sürüm: %s</string>\n    <string name=\"select_all\">Tümünü seç</string>\n    <string name=\"tutorial_online\">Öğretici (çevrim içi)</string>\n    <string name=\"showcase_tutorial_title\">Kaybolmuş gibi mi hissediyorsunuz?</string>\n    <string name=\"showcase_tutorial_description\">Çevrim içi bir öğreticimiz var. Ayarlarda bulabilirsiniz</string>\n    <string name=\"welcome_note_row_2\">Başlamak için bunu açın.</string>\n    <string name=\"unsupported_readonly_file\">Desteklenmeyen salt okunur dosya: %s</string>\n    <string name=\"show_completed_notes\">Tamamlanan notları göster</string>\n    <string name=\"sorting\">Sıralama</string>\n    <string name=\"undo\">Geri al</string>\n    <string name=\"allow_exact_reminders_summary\">Bu uygulamanın hatırlatıcıları daha güvenilir bir şekilde göndermesini sağlamak için bir sayfa açın. Yalnızca Android 12 Snow Cone\\'dan sonrası için gereklidir</string>\n    <string name=\"changelog_online\">Değişiklik günlüğü (çevrim içi)</string>\n    <string name=\"next_month\">Sonraki ay</string>\n    <string name=\"next_year\">Sonraki yıl</string>\n    <string name=\"welcome_note_row_3\">Bu uygulamanın ayrıntılı bir öğreticisi var. Şu adreste bulabilirsiniz</string>\n    <string name=\"notification_channel_name\">Notlar için hatırlatıcılar</string>\n    <string name=\"use_exact_alarms\">Tam alarmlar kullan</string>\n    <string name=\"allow_exact_reminders\">Tam hatırlatıcılara izin ver</string>\n    <string name=\"for_older_devices\">Daha eski Android aygıtları için</string>\n    <string name=\"prefs_improved_reliability\">Güvenilirliği artırmak için tercihler</string>\n    <string name=\"notification_channel_settings\">Bildirim kanalı ayarları</string>\n    <string name=\"app_about_dev_team\">Jonas Kalderstam tarafından oluşturuldu ve Campello Manuel tarafından sürdürülüyor</string>\n    <string name=\"app_about_git_repo\">Bu uygulama telif izinli bir özgür yazılımdır, https://github.com/spacecowboy/NotePad adresinde bulunur</string>\n    <string name=\"bigger_titles_summary\">Başlık ilk satır</string>\n    <string name=\"notification_channel_description\">Seçilen hatırlatıcı zamanında not içeriğini gösterir</string>\n    <string name=\"exact_alarms_summary\">Bildirim hatırlatıcılarını daha güvenilir bir şekilde göstermek için daha fazla pil kullanır</string>\n    <string name=\"bigger_titles\">Daha büyük başlıklar</string>\n    <string name=\"app_about_license\">GPLv3+ altında lisanslandı, https://www.gnu.org/licenses</string>\n    <string name=\"app_about_bugreports\">Hataları ve sorunları https://github.com/spacecowboy/NotePad/issues adresinde bildirin</string>\n    <string name=\"app_about_translations\">https://hosted.weblate.org/engage/no-nonsense-notes/ adresinde uygulamayı çevirmeye yardım edin</string>\n    <string name=\"unavailable_chose_directory\">Kullanılamıyor. Lütfen önce bir dizin seçin</string>\n    <string name=\"not_selected_yet\">Henüz seçilmedi</string>\n    <string name=\"account\">Hesap</string>\n    <string name=\"directory_summary_msg\">Dosyalar %s konumuna kaydedilir,\n\\ndosya yöneticisi uygulamanız ile erişebilirsiniz. Uygulamayı kaldırmak bu dosyaları da silecektir: notlarınızı saklamak için bir yedek alın</string>\n    <string name=\"delete_completed_tasks_question\">Tamamlanan tüm görevler silinsin mi?</string>\n    <string name=\"enable_sync\">Eşitlemeyi etkinleştir</string>\n    <string name=\"canceled_note_locked\">İptal edildi: not kilitli</string>\n    <string name=\"overwritten_in_newer_systems\">Belirli bildirim kanalı ayarları tarafından geri alınabilir</string>\n    <string name=\"open_channel_settings_description\">Bu uygulamanın bildirim ayarlarını düzenlemek için bir sayfa açın. Bunlar başka bir yerde seçilen tüm ayarları geçersiz kılar</string>\n    <string name=\"welcome_note_title\">Hoş geldiniz!</string>\n    <string name=\"notifications_blocked\">Bildirimler görünür değil, bu nedenle hatırlatıcıları görmeyeceksiniz. Buraya dokunun, izinler kategorisini bulun ve bu uygulama için bildirimleri etkinleştirin</string>\n    <string name=\"order_notes_by\">Notları sıralama ölçütü:</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-uk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "app/src/main/res/values-v26/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n\t<!-- When users increase the text size in the accessibility settings, the search suggestions\n\t popup cuts the text (it only shows the top half of the letters), because the height of the\n\t \"dropdown menu list item\" is fixed in:\n\t androidx.appcompat.R.layout.abc_search_dropdown_item_icons_2line\n\t We solve the bug with this override of the style used by \"dropdown list items\"\n\t  such as the search suggestions -->\n\t<style name=\"ReadableSearchSuggestionListItem\" parent=\"android:Widget.Material.DropDownItem\">\n\t\t<!-- we force the text to resize in order to fit the fixed height of the list item -->\n\t\t<item name=\"android:autoSizeTextType\">uniform</item>\n\t\t<item name=\"android:autoSizeMinTextSize\">5sp</item>\n\t\t<item name=\"android:autoSizeStepGranularity\">0.1sp</item>\n\t\t<item name=\"android:autoSizeMaxTextSize\">40sp</item>\n\t</style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-vec/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name_short\">Note</string>\n    <string name=\"timemachine\">Versioni vecie</string>\n    <string name=\"small\">Ceo</string>\n    <string name=\"medium\">Medio</string>\n    <string name=\"large\">Grando</string>\n    <string name=\"normal\">Normae</string>\n    <string name=\"dashclock_tomorrow\">Doman</string>\n    <string name=\"dashclock_today\">Oncò</string>\n    <string name=\"dashclock_pref_header_general\">Impostasion</string>\n    <string name=\"priority\">Priorità</string>\n    <string name=\"silent\">Siensioso</string>\n    <string name=\"selected_one\">Sèesionada na nota</string>\n    <string name=\"selected_other\">Sèesionae %d note</string>\n    <string name=\"transparency\">Trasparensa</string>\n    <string name=\"hide_date\">Scondi a data de scadensa</string>\n    <string name=\"hide_checkbox\">Scondi a spunta</string>\n    <string name=\"this_week\">Sta setimana</string>\n    <string name=\"next_5_days\">Pròsimi 5 dì</string>\n    <string name=\"date_header_completed\">Finia</string>\n    <string name=\"date_header_none\">Sensa data</string>\n    <string name=\"date_header_future\">Dopo</string>\n    <string name=\"date_header_tomorrow\">Doman</string>\n    <string name=\"date_header_today\">Oncò</string>\n    <string name=\"date_header_overdue\">Scadue</string>\n    <string name=\"completed\">Finia</string>\n    <string name=\"about\">Informasiòn</string>\n    <string name=\"clear_password\">Càva a pasword</string>\n    <string name=\"password_info\">Te poi blocare ogni nota, cosita la vede soeo chi che sa a pasword. A rimane comunque accesibie su chialtri dispositivi.</string>\n    <string name=\"done\">Finio</string>\n    <string name=\"menu_delete\">Cansea</string>\n    <string name=\"menu_deletelist\">Cansea a lista</string>\n    <string name=\"menu_preferences\">Impostasion</string>\n    <string name=\"delete_question\">Vutu cansear?</string>\n    <string name=\"delete_list_message\">Vutu cansear sta lista?</string>\n    <string name=\"title_create\">Nòva nota</string>\n    <string name=\"time\">Tempo</string>\n    <string name=\"saved\">Salvà</string>\n    <string name=\"menu_save_and_add\">Salva e meti n\\'altra nota</string>\n    <string name=\"delete_item_message\">Vuto butare via sta nota \\?</string>\n    <string name=\"delete_completed_tasks_question\">Vuto butar via tute e note completae \\?</string>\n    <string name=\"delete_items_message\">Vuto butare via tute ste note \\?</string>\n    <string name=\"editor_due_date_hint\">Data de scadensa</string>\n    <string name=\"editor_title_hint\">Titoeo</string>\n    <string name=\"sort_list_due\">Data de scadensa</string>\n    <string name=\"resolve_edit\">Modifica la nota</string>\n    <string name=\"default_style\">Stie predefinio</string>\n    <string name=\"show_items_as_tasks\">Come robe da fare</string>\n    <string name=\"show_items_as_notes\">Come note normai</string>\n    <string name=\"sort_list_default\">Ordinamento predefinio</string>\n    <string name=\"editor_note_hint\">Nota</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_updated\">Par ultimo agiornamento</string>\n    <string name=\"archive\">Archivio</string>\n    <string name=\"sort_list_manual\">Manuae</string>\n    <string name=\"restore\">Ripristina</string>\n    <string name=\"restore_to\">Ripristina su</string>\n    <string name=\"search_description\">Note e promemoria</string>\n    <string name=\"settings_theme\">Tema atuae</string>\n    <string name=\"settings_theme_dialog\">Usa il tema</string>\n    <string name=\"settings_lang\">Lengua</string>\n    <string name=\"settings_summary_theme_black\">Nèro nero</string>\n    <string name=\"settings_summary_theme_dark\">Scuro</string>\n    <string name=\"settings_summary_theme_light\">Ciaro</string>\n    <string name=\"settings_summary_theme_classic\">Clasico</string>\n    <string name=\"settings_cat_appearance\">Aspeto</string>\n    <string name=\"preference_preview_text\">El testo vien fora cosìta</string>\n    <string name=\"always\">Sempre</string>\n    <string name=\"drag_to_timetravel\">Trascina par tornare a nà version vecia</string>\n    <string name=\"settings_list\">Lista</string>\n    <string name=\"import_error\">Ghe se un problema: %s</string>\n    <string name=\"notification_channel_name\">Promemoria pae note</string>\n    <string name=\"feature_is_WIP\">Sta roba a se ancora un lavoro in corso.</string>\n    <string name=\"notification_channel_description\">Mostra a nota quando riva l\\'ora del promemoria</string>\n    <string name=\"file_picker_not_available\">L\\'app dei file no a ghe xè</string>\n    <string name=\"no_sync_method_chosen\">No ti gà scelto un metodo par sincronisare</string>\n    <string name=\"choose_backup_folder\">Scegli la cartea par salvare i backup</string>\n    <string name=\"use_exact_alarms\">Usa le notifiche precise</string>\n    <string name=\"exact_alarms_summary\">El consuma pì bateria, ma i promemoria i vien fòra pì precisi</string>\n    <string name=\"disable_battery_optimizations\">Cava l\\'otimisasion dea bateria</string>\n    <string name=\"allow_exact_reminders_summary\">Versi na pagina par dare a sta aplicasion el permeso de mostrare promemoria pì precisi. Solo per Android 12 Snow Cone o pì novi</string>\n    <string name=\"for_older_devices\">Pai teefoni pì veci</string>\n    <string name=\"battery_optimizations_active\">Par déso a sè impisada</string>\n    <string name=\"battery_optimizations_inactive\">La se sà stuada</string>\n    <string name=\"allow_exact_reminders\">Consenti i promemoria precisi</string>\n    <string name=\"notification_channel_settings\">Impostasion del canae dee notifiche</string>\n    <string name=\"libraries_used\">Librerie</string>\n    <string name=\"prefs_improved_reliability\">Par migliorar l\\'afidabilità</string>\n    <string name=\"overwritten_in_newer_systems\">Le impostasion del canae gà la precedensa su queste</string>\n    <string name=\"app_about_dev_team\">Fàta da Jonas Kalderstam e tegnua in pie da Campello Manuel</string>\n    <string name=\"app_about_bugreports\">Se la gà problemi, vien dirmeo qua https://github.com/spacecowboy/NotePad/issues</string>\n    <string name=\"app_about_license\">Licensa GPLv3+, https://www.gnu.org/licenses</string>\n    <string name=\"disable_android_hibernation\">Spegni l\\'ibernasion de Android</string>\n    <string name=\"msg_hibernation_already_off\">L\\'ibernasion de Android a xè sà SPENTA, no ti gà da far niente</string>\n    <string name=\"app_about_translations\">Dame na man a tradure sta aplicasion https://hosted.weblate.org/engage/no-nonsense-notes/</string>\n    <string name=\"disable_android_hibernation_desc\">La se na funsion che rende l\\'aplicasion inutie, la bloca i promemoria. Toca qua, sèrca na roba come \\\"sospendi attività dell\\'app\\\" e spegnia</string>\n    <string name=\"unavailable_chose_directory\">No a ghe xe. Prima scelgi na cartea</string>\n    <string name=\"notifications_enabled\">Le notifiche xè visibii</string>\n    <string name=\"notifications_blocked\">Le notifiche no xè visibii, quindi no te vedi i promemoria. Toca qua, trova a categoria \\\"permessi\\\" e abilita e notifiche par sta aplicasion</string>\n    <string name=\"move\">Sposta</string>\n    <string name=\"show_elements_as\">Mostra elementi come:</string>\n    <string name=\"order_notes_by\">Ordina note par:</string>\n    <string name=\"menu_sync\">Sincronisa</string>\n    <string name=\"menu_share\">Condividi</string>\n    <string name=\"menu_createlist\">Scomisia n\\'altra lista</string>\n    <string name=\"menu_clearcompleted\">Cava quee finie</string>\n    <string name=\"menu_setdefaultlist\">La sè quea pì importante</string>\n    <string name=\"menu_managelists\">Modifica lista</string>\n    <string name=\"search_hint\">Sèrca</string>\n    <string name=\"lists\">Liste</string>\n    <string name=\"repeat\">Ripeti</string>\n    <string name=\"once\">Nà volta</string>\n    <string name=\"permission_read_label\">Lèsi note e liste</string>\n    <string name=\"permission_read_desc\">Ghe dà a l\\'aplicasion el permeso de lesàre e note de No Nonsense Notes</string>\n    <string name=\"new_default_set\">Ti gà scelto na nuova predefinia</string>\n    <string name=\"add_item\">Meti uno nuovo</string>\n    <string name=\"app_about_git_repo\">Sta aplicasion a se open source, col codice su https://github.com/spacecowboy/NotePad</string>\n    <string name=\"changelog_online\">Lista agiornamenti (online)</string>\n    <string name=\"canceled_note_locked\">Dàssa stare: la nota xè blocada</string>\n    <string name=\"select_all\">Seesiona tuti</string>\n    <string name=\"msg_enable_notifications\">Và su impostasion -&gt; notifiche par impisare e notifiche</string>\n    <string name=\"tutorial_online\">Istrusion (online)</string>\n    <string name=\"welcome_note_row_2\">Versi questa nota par scomisiare.</string>\n    <string name=\"welcome_note_row_3\">Sta applicasion la gà anca el tutorial online. L\\'è qua</string>\n    <string name=\"unsupported_readonly_file\">File in sola letura non amesso: %s</string>\n    <string name=\"welcome_note_title\">Benvegnuo!</string>\n    <string name=\"showcase_tutorial_title\">No te capissito\\?</string>\n    <string name=\"showcase_tutorial_description\">Ghemo anca el tutorial online. Te lo trovi tra le impostasion</string>\n    <string name=\"settings_account_title\">Scegli il profio</string>\n    <string name=\"settings_account_summary\">Toca qua par cambiar profio</string>\n    <string name=\"permission_write_desc\">Ghe dà el permeso all\\'app de inserire, agiornare e cavare e note su No Nonsense Notes</string>\n    <string name=\"settings_list_dialog\">Scegli a lista</string>\n    <string name=\"import_data_question\">Tiro dentro i dati \\?</string>\n    <string name=\"navigation_drawer_close\">Sara el paneo de navigasion</string>\n    <string name=\"import_started\">Porto fòra i dati…</string>\n    <string name=\"imported_result\">Gò messo %1$d note intel %2$d liste</string>\n    <string name=\"permission_denied\">No riesso a ndar vanti: no ti me gà dato el permeso</string>\n    <string name=\"open_channel_settings_description\">Versi na pagina par modificare i permesi dee notifiche. Questi i ga la priorità su tuti chialtri</string>\n    <string name=\"notifications_visibility\">Visibiità dee notifiche</string>\n    <string name=\"not_selected_yet\">Non ancora selesionado</string>\n    <string name=\"move_to\">Sposta su</string>\n    <string name=\"password_required\">Serve a pasword</string>\n    <string name=\"select_account\">Scegli un profio</string>\n    <string name=\"password\">Pasword</string>\n    <string name=\"password_set\">Impostada la pasword</string>\n    <string name=\"moved_x_to_list\">Spostae %1$d su %2$s</string>\n    <string name=\"password_cleared\">Cavada la pasword</string>\n    <string name=\"account\">Profio</string>\n    <string name=\"lock_note\">Bloca a nota</string>\n    <string name=\"unlock_note\">Sbloca a nota</string>\n    <string name=\"locked\">Blocaa</string>\n    <string name=\"unlocked\">Sblocaa</string>\n    <string name=\"passwords_dont_match\">Le 2 pasword noe se compagne</string>\n    <string name=\"password_incorrect\">Pasword sbagliada</string>\n    <string name=\"enter_password\">Scrivi la pasword</string>\n    <string name=\"enter_new_password\">Meti n\\'altra pasword</string>\n    <string name=\"confirm_new_password\">Confermame chea pasword</string>\n    <string name=\"apply\">Conferma</string>\n    <string name=\"next_n_days\">Prosimi %d dì</string>\n    <string name=\"loading_widget\">So drio caricare sto widget…</string>\n    <string name=\"notecopied_other\">Copiae %d note</string>\n    <string name=\"notedeleted_one\">Canseada na nota</string>\n    <string name=\"background_sync\">Sincronisasion sconta</string>\n    <string name=\"snooze\">Pì tardi</string>\n    <string name=\"sound\">Suono</string>\n    <string name=\"vibrate\">Vibrasion</string>\n    <string name=\"reminders\">Promemoria</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Mostraghine el pi posibie</string>\n    <string name=\"version\">Versiòn: %s</string>\n    <string name=\"next_month\">Prosimo mese</string>\n    <string name=\"next_year\">Prosimo àno</string>\n    <string name=\"settings_cat_syncing\">Sincronisasion</string>\n    <string name=\"show_from_all_lists\">Tute e liste</string>\n    <string name=\"deleted\">Canseae</string>\n    <string name=\"notedeleted_other\">Canseae %d note</string>\n    <string name=\"permission_write_label\">Modifica note e liste</string>\n    <string name=\"navigation_drawer_open\">Versi el paneo de navigasion</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-vi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">Ghi chú</string>\n    <string name=\"title_create\">Ghi chú mới</string>\n    <string name=\"timemachine\">Máy thời gian</string>\n    <string name=\"time\">Thời gian</string>\n    <string name=\"done\">Đã xong</string>\n    <string name=\"saved\">Đã lưư</string>\n    <string name=\"menu_save_and_add\">Lưu và thêm ghi chú</string>\n    <string name=\"menu_delete\">Xoá</string>\n    <string name=\"menu_deletelist\">Xoá danh sách</string>\n    <string name=\"menu_sync\">Đồng bộ</string>\n    <string name=\"menu_share\">Chia sẻ</string>\n    <string name=\"menu_preferences\">Cài đặt</string>\n    <string name=\"menu_createlist\">Danh sách mới</string>\n    <string name=\"menu_clearcompleted\">Xoá ghi chú đã hoàn thành</string>\n    <string name=\"menu_setdefaultlist\">Đặt làm mặc định</string>\n    <string name=\"new_default_set\">Đã đặt danh sách mặc định mới</string>\n    <string name=\"menu_managelists\">Sửa danh sách</string>\n    <string name=\"add_item\">Thêm mục</string>\n    <string name=\"delete_question\">Xoá?</string>\n    <string name=\"delete_items_message\">Bạn chắc chắn muốn xoá các mục này?</string>\n    <string name=\"delete_item_message\">Bạn chắc chắn muốn xoá mục này?</string>\n    <string name=\"delete_list_message\">Bạn chắc chẵn muốn xoá danh sách?</string>\n    <string name=\"editor_due_date_hint\">Hạn chót</string>\n    <string name=\"editor_title_hint\">Tiêu đề</string>\n    <string name=\"editor_note_hint\">Ghi chú</string>\n    <string name=\"resolve_edit\">Sửa ghi chú</string>\n    <string name=\"default_style\">Kiểu mặc định</string>\n    <string name=\"show_items_as_tasks\">Xem dạng nhiệm vụ</string>\n    <string name=\"show_items_as_notes\">Xem dạng ghi chú</string>\n    <string name=\"sort_list_default\">Thứ tự sắp xếp mặc định</string>\n    <string name=\"sort_list_alphabetical\">Sắp xếp theo tên</string>\n    <string name=\"sort_list_due\">Sắp xếp theo hạn chót</string>\n    <string name=\"sort_list_updated\">Sắp xếp theo thời gian sửa đổi</string>\n    <string name=\"sort_list_manual\">Sắp xếp tự do</string>\n    <string name=\"search_description\">Ghi chú và nhiệm vụ</string>\n    <string name=\"archive\">Lưu trữ</string>\n    <string name=\"restore\">Khôi phục</string>\n    <string name=\"restore_to\">Khôi phục tới</string>\n    <string name=\"search_hint\">Tìm</string>\n    <string name=\"settings_theme\">Giao diện hiện tại</string>\n    <string name=\"settings_theme_dialog\">Dùng giao diện</string>\n    <string name=\"settings_lang\">Ngôn ngữ</string>\n    <string name=\"settings_summary_theme_black\">Đen</string>\n    <string name=\"settings_summary_theme_dark\">Tối</string>\n    <string name=\"settings_summary_theme_light\">Sáng</string>\n    <string name=\"settings_summary_theme_classic\">Cổ điển</string>\n    <string name=\"settings_cat_appearance\">Hiển thị</string>\n    <string name=\"preference_preview_text\">Chữ sẽ trông như thế này</string>\n    <string name=\"settings_account_title\">Chọn tài khoản</string>\n    <string name=\"settings_account_summary\">Chạm để đổi tài khoản</string>\n    <string name=\"settings_cat_syncing\">Đồng bộ hoá</string>\n    <string name=\"show_from_all_lists\">Tất cả danh sách</string>\n    <string name=\"lists\">Danh sách</string>\n    <string name=\"deleted\">Đã xoá</string>\n    <string name=\"repeat\">Lặp lại</string>\n    <string name=\"once\">Một lần</string>\n    <string name=\"always\">Luôn luôn</string>\n    <string name=\"permission_read_label\">đọc các ghi chú và danh sách</string>\n    <string name=\"permission_read_desc\">Cho phép ứng dụng đọc ghi chú của bạn và các danh sách có liên quan</string>\n    <string name=\"permission_write_label\">sửa các ghi chú và danh sách</string>\n    <string name=\"permission_write_desc\">Cho phép ứng dụng tạo mới, cập nhật và xoá bỏ ghi chú, danh sách</string>\n    <string name=\"settings_list_dialog\">Chọn danh sách</string>\n    <string name=\"settings_list\">Danh sách</string>\n    <string name=\"drag_to_timetravel\">Thả vào máy thời gian</string>\n    <string name=\"import_data_question\">Nhập dữ liệu?</string>\n    <string name=\"import_started\">Đang nhập dữ liệu…</string>\n    <string name=\"imported_result\">Đã nhập %1$d ghi chú từ%2$d danh sách</string>\n    <string name=\"import_error\">Đã xảy ra lỗi: %s</string>\n    <string name=\"move\">Di chuyển</string>\n    <string name=\"move_to\">Di chuyển tới</string>\n    <string name=\"moved_x_to_list\">Đã di chuyển %1$d tới %2$s</string>\n    <string name=\"lock_note\">Khoá ghi chú</string>\n    <string name=\"unlock_note\">Mở khoá ghi chú</string>\n    <string name=\"locked\">Đã khoá</string>\n    <string name=\"unlocked\">Đã mở khoá</string>\n    <string name=\"password_required\">Cần mật khẩu để mở khoá</string>\n    <string name=\"select_account\">Chọn một tài khoản</string>\n    <string name=\"password\">Mật khẩu</string>\n    <string name=\"password_set\">Đã đặt mật khẩu</string>\n    <string name=\"password_cleared\">Đã bỏ mật khẩu</string>\n    <string name=\"passwords_dont_match\">Mật khẩu chưa khớp</string>\n    <string name=\"password_incorrect\">Sai mật khẩu</string>\n    <string name=\"enter_password\">Nhập mật khẩu</string>\n    <string name=\"enter_new_password\">Nhập mật khẩu mới</string>\n    <string name=\"confirm_new_password\">Xác nhận mật khẩu mới</string>\n    <string name=\"apply\">Áp dụng</string>\n    <string name=\"clear_password\">Huỷ bỏ mật khẩu</string>\n    <string name=\"password_info\">Khi đã khoá một ghi chú, bạn phải có mật khẩu để xem và sửa ghi chú đó. Mã khoá chỉ có tác dụng trong ứng dụng này.</string>\n    <string name=\"about\">Giới thiệu</string>\n    <string name=\"sync_failed\">Đồng bộ hoá thất bại</string>\n    <string name=\"sync_login_failed\">Không thể đăng nhập vào Google Task</string>\n    <string name=\"completed\">Đã xong</string>\n    <string name=\"date_header_overdue\">Quá hạn</string>\n    <string name=\"date_header_today\">Hôm nay</string>\n    <string name=\"date_header_tomorrow\">Ngày mai</string>\n    <string name=\"date_header_future\">Sắp tới</string>\n    <string name=\"date_header_none\">Không có ngày</string>\n    <string name=\"date_header_completed\">Đã xong</string>\n    <string name=\"next_5_days\">5 ngày tới</string>\n    <string name=\"next_n_days\">%d ngày tới</string>\n    <string name=\"this_week\">Tuần này</string>\n    <string name=\"please_select_note\">Hãy chọn hoặc tạo ghi chú</string>\n    <string name=\"please_create_note\">Hãy tạo ghi chú mới</string>\n    <string name=\"hide_checkbox\">Ẩn đánh dấu</string>\n    <string name=\"hide_checkbox_summary_on\">Ẩn hộp đánh dấu cạnh ghi chú</string>\n    <string name=\"hide_checkbox_summary_off\">Hiện hộp đánh dấu cạnh ghi chú</string>\n    <string name=\"hide_date\">Ẩn hạn chót</string>\n    <string name=\"item_max_height\">Số hàng chữ tối đa của ghi chú / nhiệm vụ</string>\n    <string name=\"long_date_format\">Kiểu ngày đầy đủ</string>\n    <string name=\"short_date_format\">Kiểu ngày rút gọn</string>\n    <string name=\"select_date\">Chọn ngày</string>\n    <string name=\"localedefault\">Ngôn ngữ mặc định</string>\n    <string name=\"hide_header\">Ẩn toàn bộ tiêu đề widget</string>\n    <string name=\"transparency\">Độ trong suốt</string>\n    <string name=\"notes_shortcut\">Lối tắt ghi chú</string>\n    <string name=\"shortcut_help1\">Nếu được chọn, lối tắt sẽ tạo một ghi chú mới trong danh sách và mở ghi chú đó. Ngược lại, lối tắt sẽ mở danh sách được chọn.</string>\n    <string name=\"loading_widget\">Đang tải widget…</string>\n    <string name=\"default_list\">Danh sách mặc định</string>\n    <string name=\"please_type_before_reminder\">Hãy nhập ghi chú trước khi đặt nhắc nhở</string>\n    <string name=\"notecopied_one\">Đã sao chép 1 ghi chú</string>\n    <string name=\"notecopied_other\">Đã sao chép %d ghi chú</string>\n    <string name=\"notedeleted_one\">Đã xoá 1 ghi chú</string>\n    <string name=\"notedeleted_other\">Đã xoá %d ghi chú</string>\n    <string name=\"selected_one\">Đã chọn 1 ghi chú</string>\n    <string name=\"selected_other\">Đã chọn %d ghi chú</string>\n    <string name=\"background_sync\">Đồng bộ hoá nền</string>\n    <string name=\"background_sync_info\">Đồng bộ mỗi giờ một lần</string>\n    <string name=\"sync_on_change\">Đồng bộ khi có thay đổi</string>\n    <string name=\"sync_on_change_info\">Đồng bộ ngay sau khi sửa ghi chú</string>\n    <string name=\"sync_on_start\">Đồng bộ khi chạy ứng dụng</string>\n    <string name=\"sync_on_start_info\">Giới hạn tối thiểu là 5 phút</string>\n    <string name=\"snooze\">Hoãn</string>\n    <string name=\"silent\">Im lặng</string>\n    <string name=\"sound\">Chuông</string>\n    <string name=\"vibrate\">Rung</string>\n    <string name=\"reminders\">Nhắc nhở</string>\n    <string name=\"standard\">Chuản</string>\n    <string name=\"notification_prio_high\">Cao: Hiện hộp thoại nổi, lớn</string>\n    <string name=\"notification_prio_low\">Thấp: Ẩn hộp thoại</string>\n    <string name=\"priority\">Ưu tiên</string>\n    <string name=\"notifications\">Thông báo</string>\n    <string name=\"tasks\">Nhiệm vụ</string>\n    <string name=\"all_tasks\">Tất cả nhiệm vụ</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">Áp dụng cho các nhiệm vụ trong danh sách</string>\n    <string name=\"dashclock_show_overdue_tasks\">Hiện nhiệm vụ quá hạn</string>\n    <string name=\"dashclock_overdue_tasks_show\">Hiện các nhiệm vụ quá hạn</string>\n    <string name=\"dashclock_overdue_tasks_hide\">Ẩn các nhiệm vụ quá hạn</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">Cài đặt NoNonsense Notes</string>\n    <string name=\"dashclock_pref_header_general\">Chung</string>\n    <string name=\"dashclock_due_upper_limit_title\">Áp dụng cho các nhiệm vụ kết thúc muộn nhất vào</string>\n    <string name=\"dashclock_today\">Hôm nay</string>\n    <string name=\"dashclock_tomorrow\">Ngày mai</string>\n    <string name=\"dashclock_next7\">7 ngày tới</string>\n    <string name=\"dashclock_anytime\">Mọi lúc</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">Hiện nhiều nhất có thể</string>\n    <string name=\"dashclock_show_only_the_next_task\">Chỉ hiện nhiệm vụ tiếp theo</string>\n    <string name=\"dashclock_header_shown\">Tên danh sách được in đậm</string>\n    <string name=\"dashclock_first_task_shown\">Tiêu đề nhiệm vụ đầu tiên được in đậm</string>\n    <string name=\"dashclock_display_header\">Hiện tên danh sách</string>\n    <string name=\"editor\">Khung văn bản</string>\n    <string name=\"bold\">Đậm</string>\n    <string name=\"italic\">Nghiêng</string>\n    <string name=\"normal\">Thường</string>\n    <string name=\"title_style\">Kiểu tiêu đề</string>\n    <string name=\"title_font\">Phông chữ tiêu đề</string>\n    <string name=\"body_font\">Phông chữ ghi chú</string>\n    <string name=\"clickable_links\">Đường dẫn nhấn vào được</string>\n    <string name=\"small\">Nhỏ</string>\n    <string name=\"medium\">Vừa</string>\n    <string name=\"large\">Lớn</string>\n    <string name=\"text_size\">Cỡ chữ</string>\n    <string name=\"text\">Chữ</string>\n    <string name=\"add_a_reminder\">Thêm báo thức</string>\n    <string name=\"backup\">Sao lưu</string>\n    <string name=\"backup_import\">Nhập sao lưu</string>\n    <string name=\"backup_export\">Tạo sao lưu</string>\n    <string name=\"backup_import_msg\">Chắc chắn nhập sao lưu từ tập tin %1$s? Dữ liệu hiện tại sẽ bị xoá.</string>\n    <string name=\"backup_export_msg\">Lưu tất cả ghi chú vào tập tin %1$s?</string>\n    <string name=\"backup_import_success\">Nhập sao lưu thành công</string>\n    <string name=\"backup_file_not_found\">Không tìm thấy tập tin sao lưu</string>\n    <string name=\"backup_import_failed\">Không đọc được tập tin sao lưu</string>\n    <string name=\"backup_export_success\">Tạo sao lưu thành công</string>\n    <string name=\"backup_export_failed\">Không thể tạo sao lưu</string>\n    <string name=\"sd_card\">Thẻ SD</string>\n    <string name=\"sd_card_sync\">Đồng bộ hoá với thẻ SD</string>\n    <string name=\"sd_card_summary\">Các ghi chú được lưu cả trên thẻ SD và trong ứng dụng. Cảnh báo: Nếu xoá tập tin đồng bộ, các ghi chú sẽ bị mất!</string>\n    <string name=\"directory\">Đường dẫn</string>\n    <string name=\"cannot_write_to_directory\">Không thể ghi vào đường đẫn</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">笔记</string>\n    <string name=\"title_create\">新笔记</string>\n    <string name=\"timemachine\">时光机</string>\n    <string name=\"time\">时间</string>\n    <string name=\"done\">完成</string>\n    <string name=\"saved\">已保存</string>\n    <string name=\"menu_save_and_add\">保存并添加笔记</string>\n    <string name=\"menu_delete\">删除</string>\n    <string name=\"menu_deletelist\">删除列表</string>\n    <string name=\"menu_sync\">同步</string>\n    <string name=\"menu_share\">分享</string>\n    <string name=\"menu_preferences\">设置</string>\n    <string name=\"menu_createlist\">开始新列表</string>\n    <string name=\"menu_clearcompleted\">清除已完成</string>\n    <string name=\"menu_setdefaultlist\">设为默认列表</string>\n    <string name=\"new_default_set\">新默认设置</string>\n    <string name=\"menu_managelists\">编辑列表</string>\n    <string name=\"add_item\">添加项目</string>\n    <string name=\"delete_question\">删除？</string>\n    <string name=\"delete_items_message\">删除这些项目？</string>\n    <string name=\"delete_item_message\">删除此项目？</string>\n    <string name=\"delete_list_message\">删除此列表？</string>\n    <string name=\"editor_due_date_hint\">到期日</string>\n    <string name=\"editor_title_hint\">标题</string>\n    <string name=\"editor_note_hint\">笔记</string>\n    <string name=\"resolve_edit\">编辑笔记</string>\n    <string name=\"default_style\">默认样式</string>\n    <string name=\"show_items_as_tasks\">可勾选的任务</string>\n    <string name=\"show_items_as_notes\">简单笔记</string>\n    <string name=\"sort_list_default\">默认排序方式</string>\n    <string name=\"sort_list_alphabetical\">A-Z</string>\n    <string name=\"sort_list_due\">到期日</string>\n    <string name=\"sort_list_updated\">最近更新</string>\n    <string name=\"sort_list_manual\">手动</string>\n    <string name=\"search_description\">笔记与任务</string>\n    <string name=\"archive\">存档</string>\n    <string name=\"restore\">还原</string>\n    <string name=\"restore_to\">还原至</string>\n    <string name=\"search_hint\">搜索</string>\n    <string name=\"settings_theme\">当前主题</string>\n    <string name=\"settings_theme_dialog\">使用主题</string>\n    <string name=\"settings_lang\">语言</string>\n    <string name=\"settings_summary_theme_black\">黑色</string>\n    <string name=\"settings_summary_theme_dark\">深色</string>\n    <string name=\"settings_summary_theme_light\">浅色</string>\n    <string name=\"settings_summary_theme_classic\">经典</string>\n    <string name=\"settings_cat_appearance\">外观</string>\n    <string name=\"preference_preview_text\">这是编辑器文本的显示方式</string>\n    <string name=\"settings_account_title\">选择账号</string>\n    <string name=\"settings_account_summary\">点击切换账号</string>\n    <string name=\"settings_cat_syncing\">正在同步</string>\n    <string name=\"show_from_all_lists\">所有列表</string>\n    <string name=\"lists\">列表</string>\n    <string name=\"deleted\">已删除</string>\n    <string name=\"repeat\">重复</string>\n    <string name=\"once\">一次性</string>\n    <string name=\"always\">总是</string>\n    <string name=\"permission_read_label\">读取笔记和列表</string>\n    <string name=\"permission_read_desc\">允许应用读取 No Nonsense Notes 中的笔记和相关列表</string>\n    <string name=\"permission_write_label\">修改笔记与列表</string>\n    <string name=\"permission_write_desc\">允许应用在 No Nonsense Notes 中插入、更新及删除笔记和列表</string>\n    <string name=\"settings_list_dialog\">选择列表</string>\n    <string name=\"settings_list\">列表</string>\n    <string name=\"drag_to_timetravel\">拖到时间旅行</string>\n    <string name=\"import_data_question\">导入数据？</string>\n    <string name=\"import_started\">正在导入数据…</string>\n    <string name=\"imported_result\">已将 %1$d 条笔记导入 %2$d 个列表</string>\n    <string name=\"import_error\">出了点问题：%s</string>\n    <string name=\"move\">移动</string>\n    <string name=\"move_to\">移动到</string>\n    <string name=\"moved_x_to_list\">已将 %1$d 移至 %2$s</string>\n    <string name=\"lock_note\">锁定笔记</string>\n    <string name=\"unlock_note\">解锁笔记</string>\n    <string name=\"locked\">已锁定</string>\n    <string name=\"unlocked\">已解锁</string>\n    <string name=\"password_required\">需要密码</string>\n    <string name=\"select_account\">选择账号</string>\n    <string name=\"password\">密码</string>\n    <string name=\"password_set\">密码已设置</string>\n    <string name=\"password_cleared\">密码已清除</string>\n    <string name=\"passwords_dont_match\">密码不匹配</string>\n    <string name=\"password_incorrect\">密码错误</string>\n    <string name=\"enter_password\">输入密码</string>\n    <string name=\"enter_new_password\">输入新密码</string>\n    <string name=\"confirm_new_password\">确认新密码</string>\n    <string name=\"apply\">应用</string>\n    <string name=\"clear_password\">清除密码</string>\n    <string name=\"password_info\">任何一条笔记都可以被锁定，限制查看和修改。在其他设备上仍然可以完全访问。</string>\n    <string name=\"about\">关于</string>\n    <string name=\"sync_failed\">同步失败</string>\n    <string name=\"sync_login_failed\">无法登录使用 Google Tasks</string>\n    <string name=\"completed\">已完成</string>\n    <string name=\"date_header_overdue\">已过期</string>\n    <string name=\"date_header_today\">今天</string>\n    <string name=\"date_header_tomorrow\">明天</string>\n    <string name=\"date_header_future\">晚些时候</string>\n    <string name=\"date_header_none\">无日期</string>\n    <string name=\"date_header_completed\">已完成</string>\n    <string name=\"next_5_days\">未来 5 天</string>\n    <string name=\"next_n_days\">未来 %d 天</string>\n    <string name=\"this_week\">本周</string>\n    <string name=\"please_select_note\">请选择或者创建笔记</string>\n    <string name=\"please_create_note\">请创建笔记</string>\n    <string name=\"hide_checkbox\">隐藏复选框</string>\n    <string name=\"hide_checkbox_summary_on\">复选框已隐藏</string>\n    <string name=\"hide_checkbox_summary_off\">复选框已显示</string>\n    <string name=\"hide_date\">隐藏到期日</string>\n    <string name=\"item_max_height\">笔记/任务的最大高度（行）</string>\n    <string name=\"long_date_format\">长日期格式，面板</string>\n    <string name=\"short_date_format\">短日期格式，列表</string>\n    <string name=\"select_date\">选择日期</string>\n    <string name=\"localedefault\">设备默认值</string>\n    <string name=\"hide_header\">隐藏整个小部件标题行</string>\n    <string name=\"transparency\">透明度</string>\n    <string name=\"notes_shortcut\">笔记快捷方式</string>\n    <string name=\"shortcut_help1\">使快捷方式使用打开的文本编辑器在选定列表中打开新笔记。</string>\n    <string name=\"loading_widget\">正在加载小部件…</string>\n    <string name=\"default_list\">默认列表</string>\n    <string name=\"please_type_before_reminder\">请在添加提醒之前输入一些文本</string>\n    <string name=\"notecopied_one\">已复制一条笔记</string>\n    <string name=\"notecopied_other\">已复制 %d 条笔记</string>\n    <string name=\"notedeleted_one\">已删除一条笔记</string>\n    <string name=\"notedeleted_other\">已删除 %d 条笔记</string>\n    <string name=\"selected_one\">已选择一条笔记</string>\n    <string name=\"selected_other\">已选择 %d 条笔记</string>\n    <string name=\"background_sync\">后台同步</string>\n    <string name=\"background_sync_info\">每小时同步一次</string>\n    <string name=\"sync_on_change\">更改时同步</string>\n    <string name=\"sync_on_change_info\">在笔记更改后不久安排同步</string>\n    <string name=\"sync_on_start\">应用启动时同步</string>\n    <string name=\"sync_on_start_info\">将同步限制为每 5 分钟一次</string>\n    <string name=\"snooze\">暂停</string>\n    <string name=\"silent\">静音</string>\n    <string name=\"sound\">铃声</string>\n    <string name=\"vibrate\">振动</string>\n    <string name=\"reminders\">提醒</string>\n    <string name=\"standard\">标准</string>\n    <string name=\"notification_prio_high\">高：置顶并展开</string>\n    <string name=\"notification_prio_low\">低：隐藏并最小化</string>\n    <string name=\"priority\">优先级</string>\n    <string name=\"notifications\">通知</string>\n    <string name=\"tasks\">任务</string>\n    <string name=\"all_tasks\">所有任务</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">仅限特定列表中的任务</string>\n    <string name=\"dashclock_show_overdue_tasks\">显示过期任务</string>\n    <string name=\"dashclock_overdue_tasks_show\">过期任务已显示</string>\n    <string name=\"dashclock_overdue_tasks_hide\">过期任务已隐藏</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">设置</string>\n    <string name=\"dashclock_pref_header_general\">常规</string>\n    <string name=\"dashclock_due_upper_limit_title\">限制最迟到期的任务</string>\n    <string name=\"dashclock_today\">今天</string>\n    <string name=\"dashclock_tomorrow\">明天</string>\n    <string name=\"dashclock_next7\">未来 7 天</string>\n    <string name=\"dashclock_anytime\">所有时间</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">显示尽可能多的内容</string>\n    <string name=\"dashclock_show_only_the_next_task\">仅显示下一任务</string>\n    <string name=\"dashclock_header_shown\">加粗列表名</string>\n    <string name=\"dashclock_first_task_shown\">加粗第一个任务的标题</string>\n    <string name=\"dashclock_display_header\">显示列表名</string>\n    <string name=\"editor\">编辑</string>\n    <string name=\"bold\">加粗</string>\n    <string name=\"italic\">倾斜</string>\n    <string name=\"normal\">正常</string>\n    <string name=\"title_style\">标题样式</string>\n    <string name=\"title_font\">标题字体</string>\n    <string name=\"body_font\">正文字体</string>\n    <string name=\"clickable_links\">可点击的链接</string>\n    <string name=\"small\">小</string>\n    <string name=\"medium\">中</string>\n    <string name=\"large\">大</string>\n    <string name=\"text_size\">字体大小</string>\n    <string name=\"text\">文本</string>\n    <string name=\"add_a_reminder\">添加提醒</string>\n    <string name=\"backup\">备份</string>\n    <string name=\"backup_import\">导入备份</string>\n    <string name=\"backup_export\">导出备份</string>\n    <string name=\"backup_import_msg\">是否尝试从 %1$s 导入备份？这将清除当前数据库。</string>\n    <string name=\"backup_export_msg\">是否将所有笔记导出到 %1$s？</string>\n    <string name=\"backup_import_success\">备份已导入</string>\n    <string name=\"backup_file_not_found\">无法找到名为 NoNonsenseNotes_Backup.json 的备份文件</string>\n    <string name=\"backup_import_failed\">无法读取名为 NoNonsenseNotes_Backup.json 的备份文件</string>\n    <string name=\"backup_export_success\">备份已导出</string>\n    <string name=\"backup_export_failed\">无法写入备份文件</string>\n    <string name=\"sd_card\">SD 卡</string>\n    <string name=\"sd_card_sync\">SD 卡同步</string>\n    <string name=\"sd_card_summary\">应用和 SD 卡中的任务保持相同。删除文件将删除应用中的任务！</string>\n    <string name=\"directory\">文件夹</string>\n    <string name=\"cannot_write_to_directory\">无法写入目录</string>\n    <string name=\"notification_channel_name\">笔记提醒</string>\n    <string name=\"file_picker_not_available\">文件选择器不可用</string>\n    <string name=\"allow_exact_reminders\">允许精确提醒</string>\n    <string name=\"prefs_improved_reliability\">提高可靠性的首选项</string>\n    <string name=\"notification_channel_settings\">通知渠道设置</string>\n    <string name=\"open_channel_settings_description\">打开页面以编辑此应用的通知设置。这些将覆盖其他地方选择的任何设置</string>\n    <string name=\"feature_is_WIP\">此功能仍在进行中.</string>\n    <string name=\"notifications_blocked\">通知不可见，因此您将看不到提醒。点击此处，找到权限类别，并为此应用启用通知</string>\n    <string name=\"next_year\">下一年</string>\n    <string name=\"select_all\">全选</string>\n    <string name=\"tutorial_online\">教程（在线）</string>\n    <string name=\"welcome_note_row_3\">此应用有详细的教程。您可以在此找到</string>\n    <string name=\"unsupported_readonly_file\">不支持的只读文件：%s</string>\n    <string name=\"show_completed_notes\">显示已完成的笔记</string>\n    <string name=\"app_about_donations\">我们接受捐赠。要支持我们的工作，请在 %2$s 中查看 %1$s</string>\n    <string name=\"account\">账号</string>\n    <string name=\"bigger_titles\">更大的标题</string>\n    <string name=\"permission_denied\">无法继续：您拒绝了权限</string>\n    <string name=\"no_sync_method_chosen\">您没有选择同步方法</string>\n    <string name=\"choose_backup_folder\">选择备份文件夹</string>\n    <string name=\"disable_battery_optimizations\">关闭电池优化</string>\n    <string name=\"app_about_dev_team\">由 Jonas Kalderstam 创建，由 Campello Manuel 维护</string>\n    <string name=\"msg_hibernation_already_off\">Android 休眠已关闭，您不需要这个</string>\n    <string name=\"msg_enable_notifications\">转到设置 -&gt; 通知以启用通知</string>\n    <string name=\"notifications_visibility\">通知可见性</string>\n    <string name=\"enable_sync_desc\">启用同步功能并允许选择同步源。备份不受影响</string>\n    <string name=\"show_elements_as\">将项目显示为：</string>\n    <string name=\"version\">版本：%s</string>\n    <string name=\"next_month\">下个月</string>\n    <string name=\"showcase_tutorial_title\">迷路了？</string>\n    <string name=\"showcase_tutorial_description\">我们有在线教程。可在设置中找到</string>\n    <string name=\"welcome_note_title\">欢迎！</string>\n    <string name=\"welcome_note_row_2\">打开这个即可开始。</string>\n    <string name=\"notification_channel_description\">显示所选提醒时的笔记内容</string>\n    <string name=\"battery_optimizations_inactive\">电池优化关闭</string>\n    <string name=\"overwritten_in_newer_systems\">可以通过某些通知渠道设置撤销</string>\n    <string name=\"app_about_translations\">在 https://hosted.weblate.org/engage/no-nonsense-notes/ 帮助翻译本应用</string>\n    <string name=\"use_exact_alarms\">使用精确闹钟</string>\n    <string name=\"exact_alarms_summary\">使用更多电量以更可靠的方式显示通知提醒</string>\n    <string name=\"battery_optimizations_active\">电池优化打开</string>\n    <string name=\"allow_exact_reminders_summary\">打开页面，允许此应用更可靠地发送提醒。仅在 Android 12 Snow Cone 之后需要</string>\n    <string name=\"for_older_devices\">适用于较旧的 Android 设备</string>\n    <string name=\"libraries_used\">库</string>\n    <string name=\"app_about_git_repo\">此应用是有 Copyleft 的自由软件，可在 https://github.com/spacecowboy/NotePad 找到</string>\n    <string name=\"disable_android_hibernation\">禁用 Android 休眠</string>\n    <string name=\"app_about_license\">许可 GPLV3+，https://www.gnu.org/licenses</string>\n    <string name=\"app_about_bugreports\">在 https://github.com/spacecowboy/NotePad/issues 报告错误和问题</string>\n    <string name=\"notifications_enabled\">通知可见</string>\n    <string name=\"disable_android_hibernation_desc\">这一功能使本应用不太可靠。它可能会阻止您的提醒。打开此页面，查找类似未使用时暂停应用活动的内容，然后将其关闭</string>\n    <string name=\"unavailable_chose_directory\">不可用。请先选择一个目录</string>\n    <string name=\"not_selected_yet\">尚未选择</string>\n    <string name=\"bigger_titles_summary\">标题是第一行</string>\n    <string name=\"sorting\">排序</string>\n    <string name=\"undo\">撤销</string>\n    <string name=\"enable_sync\">启用同步</string>\n    <string name=\"changelog_online\">变更日志（在线）</string>\n    <string name=\"order_notes_by\">笔记顺序：</string>\n    <string name=\"canceled_note_locked\">已取消：笔记已锁定</string>\n    <string name=\"directory_summary_msg\">文件保存在 %s\n\\n您可以使用文件管理器访问。卸载应用也会删除这些文件：要保存笔记，请备份</string>\n    <string name=\"delete_completed_tasks_question\">删除所有已完成的任务？</string>\n    <string name=\"navigation_drawer_open\">打开导航抽屉</string>\n    <string name=\"navigation_drawer_close\">关闭导航抽屉</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  --><resources>\n    <string name=\"app_name_short\">便箋</string>\n    <string name=\"title_create\">新建便箋</string>\n    <string name=\"timemachine\">時光機</string>\n    <string name=\"time\">時間</string>\n    <string name=\"done\">完成</string>\n    <string name=\"saved\">已儲存</string>\n    <string name=\"menu_save_and_add\">保存並打開一個新便箋</string>\n    <string name=\"menu_delete\">刪除</string>\n    <string name=\"menu_deletelist\">刪除清單</string>\n    <string name=\"menu_sync\">刷新</string>\n    <string name=\"menu_share\">分享</string>\n    <string name=\"menu_preferences\">設定</string>\n    <string name=\"menu_createlist\">新增清單</string>\n    <string name=\"menu_clearcompleted\">清除已完成</string>\n    <string name=\"menu_setdefaultlist\">設為預設清單</string>\n    <string name=\"menu_managelists\">管理清單</string>\n    <string name=\"add_item\">建立項目</string>\n    <string name=\"delete_question\">刪除？</string>\n    <string name=\"delete_items_message\">您確信要刪除這些項目嗎？</string>\n    <string name=\"delete_item_message\">您確信要刪除這個項目嗎？</string>\n    <string name=\"delete_list_message\">您確信要刪除這個清單嗎？</string>\n    <string name=\"editor_due_date_hint\">到期日</string>\n    <string name=\"editor_title_hint\">標題</string>\n    <string name=\"editor_note_hint\">便箋</string>\n    <string name=\"resolve_edit\">編輯便箋</string>\n    <string name=\"default_style\">默認樣式</string>\n    <string name=\"show_items_as_tasks\">以任務顯示項目</string>\n    <string name=\"show_items_as_notes\">以便箋顯示項目</string>\n    <string name=\"sort_list_default\">默認排序方法</string>\n    <string name=\"sort_list_alphabetical\">按字母表順序</string>\n    <string name=\"sort_list_due\">按過期日順序</string>\n    <string name=\"sort_list_updated\">按最近更新順序</string>\n    <string name=\"sort_list_manual\">手動排序</string>\n    <string name=\"search_description\">便箋和任務</string>\n    <string name=\"archive\">封存</string>\n    <string name=\"restore\">還原</string>\n    <string name=\"restore_to\">還原至</string>\n    <string name=\"search_hint\">搜尋</string>\n    <string name=\"settings_theme\">當前主題</string>\n    <string name=\"settings_theme_dialog\">使用主題</string>\n    <string name=\"settings_lang\">語言</string>\n    <string name=\"settings_summary_theme_black\">黑色</string>\n    <string name=\"settings_summary_theme_dark\">深色</string>\n    <string name=\"settings_summary_theme_light\">淺色</string>\n    <string name=\"settings_summary_theme_classic\">經典</string>\n    <string name=\"settings_cat_appearance\">外觀</string>\n    <string name=\"preference_preview_text\">我能吞下玻璃而不傷身體</string>\n    <string name=\"settings_account_title\">選定一個帳戶</string>\n    <string name=\"settings_account_summary\">點擊切換帳戶</string>\n    <string name=\"settings_cat_syncing\">同步</string>\n    <string name=\"show_from_all_lists\">所有清單</string>\n    <string name=\"deleted\">已刪除</string>\n    <string name=\"repeat\">重複</string>\n    <string name=\"once\">一次性</string>\n    <string name=\"permission_read_label\">閱讀便箋與清單</string>\n    <string name=\"permission_read_desc\">允許本應用讀取您的便箋及相關清單</string>\n    <string name=\"permission_write_label\">修改便箋和清單</string>\n    <string name=\"permission_write_desc\">允許本應用新增，更新及刪除便箋和清單</string>\n    <string name=\"settings_list_dialog\">選擇一個清單</string>\n    <string name=\"settings_list\">清單</string>\n    <string name=\"import_data_question\">匯入數據？</string>\n    <string name=\"import_started\">正在匯入數據…</string>\n    <string name=\"imported_result\">已匯入 %1$d 條便箋至 %2$d 清單</string>\n    <string name=\"import_error\">出現了一些問題：%s</string>\n    <string name=\"lock_note\">鎖定便箋</string>\n    <string name=\"unlock_note\">解鎖便箋</string>\n    <string name=\"locked\">已鎖定</string>\n    <string name=\"unlocked\">已解鎖</string>\n    <string name=\"password_required\">需要密碼</string>\n    <string name=\"select_account\">選擇一個賬號</string>\n    <string name=\"password\">密碼</string>\n    <string name=\"password_set\">密碼已設定</string>\n    <string name=\"password_cleared\">密碼已清除</string>\n    <string name=\"passwords_dont_match\">密碼不匹配</string>\n    <string name=\"password_incorrect\">密碼錯誤</string>\n    <string name=\"enter_password\">輸入密碼</string>\n    <string name=\"enter_new_password\">輸入新密碼</string>\n    <string name=\"confirm_new_password\">核對新密碼</string>\n    <string name=\"apply\">套用</string>\n    <string name=\"clear_password\">清除密碼</string>\n    <string name=\"password_info\">設定密碼後查看任何鎖定的便箋時均需要輸入密碼。密碼只會保護便箋正文部分，因此便箋標題與到期日仍然可以直接訪問。密碼同樣不會以任何方式加密便箋，所以仍然可以直接在 Google 郵箱與日曆應用中查看到。如果您忘記了密碼，可以先手動同步一次，然後重新安裝本應用。</string>\n    <string name=\"about\">關於</string>\n    <string name=\"sync_failed\">同步失敗</string>\n    <string name=\"sync_login_failed\">登入失敗，無法連接到 Google 工作表</string>\n    <string name=\"completed\">已完成</string>\n    <string name=\"date_header_overdue\">過期日</string>\n    <string name=\"date_header_today\">今天</string>\n    <string name=\"date_header_tomorrow\">明天</string>\n    <string name=\"date_header_future\">晚些時候</string>\n    <string name=\"date_header_none\">無日期</string>\n    <string name=\"date_header_completed\">已完成</string>\n    <string name=\"please_select_note\">請選擇或者新增一個便箋</string>\n    <string name=\"please_create_note\">請新建一個便箋</string>\n    <string name=\"hide_checkbox\">隱藏複選框</string>\n    <string name=\"hide_checkbox_summary_on\">複選框會被隱藏</string>\n    <string name=\"hide_checkbox_summary_off\">複選框會被顯示</string>\n    <string name=\"hide_date\">隱藏過期日</string>\n    <string name=\"item_max_height\">便箋/任務的</string>\n    <string name=\"long_date_format\">長日期格式</string>\n    <string name=\"short_date_format\">短日期格式</string>\n    <string name=\"localedefault\">使用所在地區預設值</string>\n    <string name=\"hide_header\">隱藏整個小部件標題行</string>\n    <string name=\"transparency\">透明度</string>\n    <string name=\"notes_shortcut\">便箋捷徑</string>\n    <string name=\"shortcut_help1\">如果選定，快捷方式將會在選中的清單創建一個便箋並打開編輯器。如果不選中，快捷方式只打開選定的清單。</string>\n    <string name=\"loading_widget\">正在加載小部件…</string>\n    <string name=\"default_list\">默認清單</string>\n    <string name=\"please_type_before_reminder\">請在設定提醒前輸入一些文字</string>\n    <string name=\"notecopied_other\">已複製 %d 個便箋</string>\n    <string name=\"notedeleted_other\">已刪除 %d 個便箋</string>\n    <string name=\"selected_other\">已選擇 %d 個便箋</string>\n    <string name=\"background_sync\">後台同步</string>\n    <string name=\"background_sync_info\">每小時同步一次</string>\n    <string name=\"sync_on_change\">變更時同步</string>\n    <string name=\"sync_on_change_info\">對便箋作出變更後立即同步</string>\n    <string name=\"sync_on_start\">應用啟動時同步</string>\n    <string name=\"sync_on_start_info\">只在初次啟動時同步，再次打開應用時不再同步</string>\n    <string name=\"snooze\">打盹</string>\n    <string name=\"silent\">靜音</string>\n    <string name=\"sound\">鈴聲</string>\n    <string name=\"vibrate\">震動</string>\n    <string name=\"reminders\">提醒</string>\n    <string name=\"standard\">標準</string>\n    <string name=\"notification_prio_high\">高級別：置頂並展開詳情</string>\n    <string name=\"notification_prio_low\">低級別：隱藏並最小化顯示</string>\n    <string name=\"priority\">優先級</string>\n    <string name=\"notifications\">通知</string>\n    <string name=\"dashclock_restrict_to_tasks_in_a_specific_list\">僅限特定清單中的任務</string>\n    <string name=\"dashclock_show_overdue_tasks\">顯示已到期任務</string>\n    <string name=\"dashclock_overdue_tasks_show\">已到期任務將會顯示</string>\n    <string name=\"dashclock_overdue_tasks_hide\">已到期任務將會隱藏</string>\n    <string name=\"dashclock_title_activity_tasks_settings\">NoNonsense 便箋設定</string>\n    <string name=\"dashclock_pref_header_general\">一般</string>\n    <string name=\"dashclock_due_upper_limit_title\">僅限最晚到期的任務</string>\n    <string name=\"dashclock_today\">今天</string>\n    <string name=\"dashclock_tomorrow\">明天</string>\n    <string name=\"dashclock_next7\">一周內</string>\n    <string name=\"dashclock_anytime\">所有時間</string>\n    <string name=\"dashclock_will_show_as_many_as_possible\">將會顯示更多內容</string>\n    <string name=\"dashclock_show_only_the_next_task\">只顯示下一任務</string>\n    <string name=\"dashclock_header_shown\">清單名稱將以粗體顯示</string>\n    <string name=\"dashclock_first_task_shown\">第一個任務的標題將加粗顯示</string>\n    <string name=\"dashclock_display_header\">顯示標題</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/xml/app_pref_backup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<PreferenceScreen\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\tandroid:key=\"app_preference_backup\">\n\n\t<!-- launch the android file picker to choose a directory -->\n\t<Preference\n\t\tandroid:icon=\"@drawable/ic_folder_24dp\"\n\t\tandroid:key=\"key_backup_dir_uri\"\n\t\tapp:singleLineTitle=\"false\"\n\t\tandroid:summary=\"added in BackupPrefs.java onUriDirPrefChange()\"\n\t\tandroid:title=\"@string/choose_backup_folder\"/>\n\n\t<Preference\n\t\tandroid:key=\"backup_import\"\n\t\tandroid:icon=\"@drawable/ic_import\"\n\t\tandroid:title=\"@string/backup_import\"/>\n\n\t<Preference\n\t\tandroid:key=\"backup_export\"\n\t\tandroid:icon=\"@drawable/ic_export\"\n\t\tandroid:title=\"@string/backup_export\"/>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/app_pref_headers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<!-- a list of all the categories in the preferences page.\n \t It is inflated by IndexPrefs.java,\n \t which lives in the layout of activity_settings.xml,\n \t which is inflated by PrefsActivity.java -->\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<Preference\n\t\tandroid:fragment=\"com.nononsenseapps.notepad.prefs.AppearancePrefs\"\n\t\tandroid:icon=\"@drawable/ic_brightness_6_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/settings_cat_appearance\"/>\n\t<Preference\n\t\tandroid:fragment=\"com.nononsenseapps.notepad.prefs.ListPrefs\"\n\t\tandroid:icon=\"@drawable/ic_folder_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/settings_list\"/>\n\t<Preference\n\t\tandroid:fragment=\"com.nononsenseapps.notepad.prefs.SyncPrefs\"\n\t\tandroid:icon=\"@drawable/ic_refresh_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/settings_cat_syncing\"/>\n\t<Preference\n\t\tandroid:fragment=\"com.nononsenseapps.notepad.prefs.NotificationPrefs\"\n\t\tandroid:icon=\"@drawable/ic_alarm_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/notifications\"/>\n\t<Preference\n\t\tandroid:fragment=\"com.nononsenseapps.notepad.prefs.PasswordPrefs\"\n\t\tandroid:icon=\"@drawable/ic_lock_closed_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/password\"/>\n\t<Preference\n\t\tandroid:fragment=\"com.nononsenseapps.notepad.prefs.BackupPrefs\"\n\t\tandroid:icon=\"@drawable/ic_sd_storage_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/backup\"/>\n\t<Preference\n\t\tandroid:icon=\"@drawable/ic_help_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/tutorial_online\">\n\t\t<intent android:action=\"android.intent.action.VIEW\"\n\t\t\tandroid:data=\"https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md\"/>\n\t</Preference>\n\t<Preference\n\t\tandroid:icon=\"@drawable/ic_archive_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/changelog_online\">\n\t\t<intent android:action=\"android.intent.action.VIEW\"\n\t\t\tandroid:data=\"https://github.com/spacecowboy/NotePad/releases\"/>\n\t</Preference>\n\t<Preference\n\t\tandroid:fragment=\"com.nononsenseapps.notepad.prefs.AboutPrefs\"\n\t\tandroid:icon=\"@drawable/ic_info_24dp\"\n\t\tandroid:summary=\"\"\n\t\tandroid:title=\"@string/about\"/>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/app_pref_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<PreferenceScreen\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\tandroid:key=\"preference_screen_list\">\n\n\t<ListPreference\n\t\tandroid:key=\"key_default_list_id\"\n\t\tandroid:title=\"@string/default_list\"/>\n\n\t<SeekBarPreference\n\t\tandroid:defaultValue=\"3\"\n\t\tandroid:key=\"@string/key_pref_item_max_height\"\n\t\tandroid:max=\"99\"\n\t\tapp:min=\"1\"\n\t\tapp:showSeekBarValue=\"true\"\n\t\tapp:singleLineTitle=\"false\"\n\t\tandroid:title=\"@string/item_max_height\"/>\n\n\t<ListPreference\n\t\tandroid:defaultValue=\"@string/default_sorttype\"\n\t\tandroid:entries=\"@array/sorting_preference\"\n\t\tandroid:entryValues=\"@array/sortingvalues_preference\"\n\t\tandroid:key=\"@string/pref_sorttype\"\n\t\tandroid:summary=\"Selected option should be here\"\n\t\tandroid:title=\"@string/sort_list_default\"/>\n\n\t<ListPreference\n\t\tandroid:defaultValue=\"@string/default_listtype\"\n\t\tandroid:entries=\"@array/listtype_preference\"\n\t\tandroid:entryValues=\"@array/listtypevalues_preference\"\n\t\tandroid:key=\"@string/pref_listtype\"\n\t\tandroid:summary=\"Selected option should be here\"\n\t\tandroid:title=\"@string/default_style\"/>\n\n\t<!-- Depends on listtype in code. See ListPrefs.java -->\n\t<SwitchPreference\n\t\tandroid:defaultValue=\"false\"\n\t\tapp:singleLineTitle=\"false\"\n\t\tandroid:key=\"@string/pref_hidecheckboxes\"\n\t\tandroid:summaryOff=\"@string/hide_checkbox_summary_off\"\n\t\tandroid:summaryOn=\"@string/hide_checkbox_summary_on\"\n\t\tandroid:title=\"@string/hide_checkbox\"/>\n\n\t<PreferenceCategory\n\t\tandroid:title=\"@string/text\">\n\n\t\t<SwitchPreference\n\t\t\tandroid:defaultValue=\"true\"\n\t\t\tandroid:key=\"@string/pref_list_links\"\n\t\t\tandroid:summary=\"\"\n\t\t\tandroid:title=\"@string/clickable_links\"/>\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"1\"\n\t\t\tandroid:dialogTitle=\"@string/text_size\"\n\t\t\tandroid:entries=\"@array/fontsize_entries\"\n\t\t\tandroid:entryValues=\"@array/fontsize_values\"\n\t\t\tandroid:key=\"@string/pref_list_fontsize\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/text_size\"/>\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"1\"\n\t\t\tandroid:dialogTitle=\"@string/title_style\"\n\t\t\tandroid:entries=\"@array/fontstyle_entries\"\n\t\t\tandroid:entryValues=\"@array/fontstyle_values\"\n\t\t\tandroid:key=\"@string/pref_list_title_fontstyle\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/title_style\"/>\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"1\"\n\t\t\tandroid:dialogTitle=\"@string/title_font\"\n\t\t\tandroid:entries=\"@array/fontfamily_entries\"\n\t\t\tandroid:entryValues=\"@array/fontfamily_values\"\n\t\t\tandroid:key=\"@string/pref_list_title_fontfamily\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/title_font\"/>\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"0\"\n\t\t\tandroid:dialogTitle=\"@string/body_font\"\n\t\t\tandroid:entries=\"@array/fontfamily_entries\"\n\t\t\tandroid:entryValues=\"@array/fontfamily_values\"\n\t\t\tandroid:key=\"@string/pref_list_body_fontfamily\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/body_font\"/>\n\n\t</PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/app_pref_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n  -->\n\n<PreferenceScreen\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:key=\"preference_screen\">\n\n\t<PreferenceCategory android:title=\"@string/dashclock_pref_header_general\">\n\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"light_ab\"\n\t\t\tandroid:dialogTitle=\"@string/settings_theme_dialog\"\n\t\t\tandroid:entries=\"@array/theme_preference\"\n\t\t\tandroid:entryValues=\"@array/themevalues_preference\"\n\t\t\tandroid:key=\"key_current_theme\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/settings_theme\"/>\n\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"\"\n\t\t\tandroid:dialogTitle=\"@string/settings_lang_dialog\"\n\t\t\tandroid:key=\"@string/pref_locale\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/settings_lang\"/>\n\n\t\t<!-- Cant set first day of week on Square's calendar, so remove option -->\n\t\t<!--\n\t\t <ListPreference\n\t\tandroid:defaultValue=\"@string/preferences_week_start_day_default\"\n\t\tandroid:dialogTitle=\"@string/preferences_week_start_day_dialog\"\n\t\tandroid:entries=\"@array/preferences_week_start_day_labels\"\n\t\tandroid:entryValues=\"@array/preferences_week_start_day_values\"\n\t\tandroid:key=\"preferences_week_start_day\"\n\t\tandroid:title=\"@string/preferences_week_start_day_title\" />\n\t\t-->\n\t\t<ListPreference\n\t\t\tandroid:key=\"@string/key_pref_dateformat_long\"\n\t\t\tandroid:title=\"@string/long_date_format\"/>\n\t\t<ListPreference\n\t\t\tandroid:key=\"@string/key_pref_dateformat_short\"\n\t\t\tandroid:title=\"@string/short_date_format\"/>\n\t</PreferenceCategory>\n\n\t<PreferenceCategory android:title=\"@string/editor\">\n\t\t<SwitchPreference\n\t\t\tandroid:defaultValue=\"true\"\n\t\t\tandroid:key=\"@string/pref_editor_links\"\n\t\t\tandroid:summary=\"\"\n\t\t\tandroid:title=\"@string/clickable_links\"/>\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"1\"\n\t\t\tandroid:dialogTitle=\"@string/text_size\"\n\t\t\tandroid:entries=\"@array/fontsize_entries\"\n\t\t\tandroid:entryValues=\"@array/fontsize_values\"\n\t\t\tandroid:key=\"@string/pref_editor_fontsize\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/text_size\"/>\n\t\t<SwitchPreference\n\t\t\tandroid:defaultValue=\"true\"\n\t\t\tandroid:key=\"@string/pref_editor_biggertitles\"\n\t\t\tandroid:summary=\"@string/bigger_titles_summary\"\n\t\t\tandroid:title=\"@string/bigger_titles\"/>\n\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"0\"\n\t\t\tandroid:dialogTitle=\"@string/title_style\"\n\t\t\tandroid:entries=\"@array/fontstyle_entries\"\n\t\t\tandroid:entryValues=\"@array/fontstyle_values\"\n\t\t\tandroid:key=\"@string/pref_editor_title_fontstyle\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/title_style\"/>\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"2\"\n\t\t\tandroid:dialogTitle=\"@string/title_font\"\n\t\t\tandroid:entries=\"@array/fontfamily_entries\"\n\t\t\tandroid:entryValues=\"@array/fontfamily_values\"\n\t\t\tandroid:key=\"@string/pref_editor_title_fontfamily\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/title_font\"/>\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"0\"\n\t\t\tandroid:dialogTitle=\"@string/body_font\"\n\t\t\tandroid:entries=\"@array/fontfamily_entries\"\n\t\t\tandroid:entryValues=\"@array/fontfamily_values\"\n\t\t\tandroid:key=\"@string/pref_editor_body_fontfamily\"\n\t\t\tandroid:summary=\"Selected option should be here\"\n\t\t\tandroid:title=\"@string/body_font\"/>\n\t</PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/app_pref_notifications.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation, either version 3 of the License, or\n  (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n-->\n\n<PreferenceScreen\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\tandroid:key=\"app_preference_screen_notifications\">\n\n\t<!-- in API >=26, open the system's notification channel pref. page, instead of using\n\t our notification preferences -->\n\t<Preference\n\t\tandroid:key=\"@string/key_pref_notif_channel_settings\"\n\t\tandroid:summary=\"@string/open_channel_settings_description\"\n\t\tandroid:persistent=\"false\"\n\t\tandroid:title=\"@string/notification_channel_settings\"/>\n\n\t<!-- shows if notifications permissions are granted -->\n\t<Preference\n\t\tandroid:key=\"@string/key_pref_notif_visibility\"\n\t\tandroid:title=\"@string/notifications_visibility\"\n\t\tandroid:summary=\"fill it in NotificationPrefs.java\"\n\t\tandroid:persistent=\"false\"/>\n\n\t<!-- these 3 settings work on android API versions < API 26, which we still support.\n\t\t in newer androids, these values are overwritten by the notification channel settings -->\n\t<PreferenceCategory\n\t\tandroid:enabled=\"true\"\n\t\tandroid:key=\"key_pref_cat_notif_old\"\n\t\tandroid:persistent=\"false\"\n\t\tandroid:title=\"@string/for_older_devices\"\n\t\tapp:initialExpandedChildrenCount=\"1\">\n\n\t\t<!-- because PreferenceCategory can't wrap its summary! -->\n\t\t<Preference android:summary=\"@string/overwritten_in_newer_systems\"/>\n\n\t\t<!-- RingtonePreference is not supported in androidx: we use the official hack,\n\t     see NotificationPrefs.java -->\n\t\t<Preference\n\t\t\tandroid:key=\"@string/key_pref_ringtone\"\n\t\t\tandroid:summary=\"filled in NotificationPrefs.java\"\n\t\t\tandroid:title=\"@string/sound\"/>\n\n\t\t<CheckBoxPreference\n\t\t\tandroid:defaultValue=\"false\"\n\t\t\tandroid:key=\"@string/key_pref_vibrate\"\n\t\t\tandroid:title=\"@string/vibrate\"/>\n\n\t\t<ListPreference\n\t\t\tandroid:defaultValue=\"0\"\n\t\t\tandroid:entries=\"@array/notification_prio_entries\"\n\t\t\tandroid:entryValues=\"@array/notification_prio_values\"\n\t\t\tandroid:key=\"@string/key_pref_prio\"\n\t\t\tandroid:title=\"@string/priority\"/>\n\n\t</PreferenceCategory>\n\n\t<PreferenceCategory\n\t\tandroid:enabled=\"true\"\n\t\tandroid:persistent=\"false\"\n\t\tandroid:title=\"@string/prefs_improved_reliability\">\n\n\t\t<!-- if we use .setExactAndAllowWhileIdle() to ignore doze mode and get alarms consistently -->\n\t\t<SwitchPreference\n\t\t\tandroid:defaultValue=\"false\"\n\t\t\tandroid:key=\"@string/key_pref_should_use_exact_alarms\"\n\t\t\tandroid:summary=\"@string/exact_alarms_summary\"\n\t\t\tandroid:title=\"@string/use_exact_alarms\"/>\n\n\t\t<!-- The user clicks this to open a system page to disable battery optimizations -->\n\t\t<Preference\n\t\t\tandroid:dependency=\"@string/key_pref_should_use_exact_alarms\"\n\t\t\tandroid:key=\"@string/key_pref_ignore_battery_optimizations\"\n\t\t\tandroid:summary=\"filled in NotificationPrefs.java\"\n\t\t\tandroid:title=\"@string/disable_battery_optimizations\"/>\n\n\t\t<!-- The user clicks this to open a system page to allow exact reminders -->\n\t\t<Preference\n\t\t\tandroid:dependency=\"@string/key_pref_should_use_exact_alarms\"\n\t\t\tandroid:key=\"@string/key_pref_allow_exact_reminders\"\n\t\t\tandroid:summary=\"@string/allow_exact_reminders_summary\"\n\t\t\tandroid:title=\"@string/allow_exact_reminders\"/>\n\n\t\t<!-- The user clicks this to open a system page to disable app hibernation -->\n\t\t<Preference\n\t\t\tandroid:persistent=\"false\"\n\t\t\tandroid:dependency=\"@string/key_pref_should_use_exact_alarms\"\n\t\t\tandroid:key=\"@string/key_pref_disable_hibernation\"\n\t\t\tandroid:summary=\"@string/disable_android_hibernation_desc\"\n\t\t\tandroid:title=\"@string/disable_android_hibernation\"/>\n\n\t</PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/app_pref_sync.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2014 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<PreferenceScreen\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:key=\"app_preference_screen_sync\">\n\n\t<!-- if ANY form of synchronization is enabled. Can disable every line of sync code,\n\t to make the app faster for those who won't use any form of synchronization  -->\n\t<SwitchPreference\n\t\tandroid:defaultValue=\"false\"\n\t\tandroid:icon=\"@drawable/ic_refresh_24dp\"\n\t\tandroid:key=\"@string/key_pref_sync_enabled_master\"\n\t\tandroid:summary=\"@string/enable_sync_desc\"\n\t\tandroid:title=\"@string/enable_sync\"/>\n\n\t<PreferenceCategory\n\t\tandroid:dependency=\"@string/key_pref_sync_enabled_master\"\n\t\tandroid:key=\"pref_sdcard_section_key\"\n\t\tandroid:persistent=\"false\"\n\t\tandroid:title=\"@string/sd_card\">\n\n\t\t<SwitchPreference\n\t\t\tandroid:defaultValue=\"false\"\n\t\t\tandroid:icon=\"@drawable/ic_folder_24dp\"\n\t\t\tandroid:key=\"pref_sync_sd_enabled\"\n\t\t\tandroid:summary=\"@string/sd_card_summary\"\n\t\t\tandroid:title=\"@string/sd_card_sync\"/>\n\n\t\t<!-- The code uses File objects instead of URIs, so enabling this is just too\n\t \t\t\tmuch work. I deleted the related java code in PR #455\n\t\t\t\tIf anyone wants to help, remove android:enabled=\"false\" and start testing.\n\t\t<Preference\n\t\t\tandroid:dependency=\"pref_sync_sd_enabled\"\n\t\t\tandroid:key=\"pref_sync_sd_dir_uri\"\n\t\t\tandroid:enabled=\"false\"\n\t\t\tandroid:summary=\"@string/directory_summary\"\n\t\t\tandroid:title=\"@string/directory\"/>\n\t\t-->\n\n\t\t<!-- Warn users that the org folder is hardcoded. See SyncPrefs.java -->\n\t\t<Preference\n\t\t\tandroid:icon=\"@drawable/ic_info_24dp\"\n\t\t\tandroid:key=\"pref_sdcard_sync_info\"\n\t\t\tandroid:summary=\"Filled in SyncPrefs.java\"\n\t\t\tandroid:title=\"@string/directory\"/>\n\n\t</PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/dashclock_pref_general.xml",
    "content": "<!-- preferences for dashclock settings. See TaskSettingsFragment.java -->\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->\n\t<ListPreference\n\t\tandroid:defaultValue=\"-1\"\n\t\tandroid:key=\"list_spinner\"\n\t\tandroid:negativeButtonText=\"@null\"\n\t\tandroid:positiveButtonText=\"@null\"\n\t\tandroid:title=\"@string/dashclock_restrict_to_tasks_in_a_specific_list\"/>\n\t<ListPreference\n\t\tandroid:defaultValue=\"@string/dashclock_pref_today\"\n\t\tandroid:entries=\"@array/due_upper_limit_entries\"\n\t\tandroid:entryValues=\"@array/due_upper_limit_values\"\n\t\tandroid:key=\"list_due_upper_limit\"\n\t\tandroid:title=\"@string/dashclock_due_upper_limit_title\"/>\n\n\t<CheckBoxPreference\n\t\tandroid:defaultValue=\"true\"\n\t\tandroid:key=\"show_overdue\"\n\t\tandroid:summaryOff=\"@string/dashclock_overdue_tasks_hide\"\n\t\tandroid:summaryOn=\"@string/dashclock_overdue_tasks_show\"\n\t\tandroid:title=\"@string/dashclock_show_overdue_tasks\"/>\n\n\t<!-- show single item only -->\n\t<CheckBoxPreference\n\t\tandroid:defaultValue=\"false\"\n\t\tandroid:key=\"show_single_only\"\n\t\tandroid:summaryOff=\"@string/dashclock_will_show_as_many_as_possible\"\n\t\tandroid:summaryOn=\"@string/dashclock_will_only_show_the_task_due_first\"\n\t\tandroid:title=\"@string/dashclock_show_only_the_next_task\"/>\n\n\t<!-- Show header -->\n\t<CheckBoxPreference\n\t\tandroid:defaultValue=\"true\"\n\t\tandroid:key=\"show_header\"\n\t\tandroid:summaryOff=\"@string/dashclock_first_task_shown\"\n\t\tandroid:summaryOn=\"@string/dashclock_header_shown\"\n\t\tandroid:title=\"@string/dashclock_display_header\"/>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/listwidgetinfo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<appwidget-provider xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:configure=\"com.nononsenseapps.notepad.widget.list.ListWidgetConfig\"\n\tandroid:initialKeyguardLayout=\"@layout/widget_layout\"\n\tandroid:initialLayout=\"@layout/widget_layout\"\n\tandroid:minWidth=\"180dp\"\n\tandroid:minHeight=\"110dp\"\n\tandroid:minResizeWidth=\"110dp\"\n\tandroid:minResizeHeight=\"110dp\"\n\tandroid:previewImage=\"@drawable/preview_ori_portrait\"\n\tandroid:resizeMode=\"horizontal|vertical\"\n\tandroid:updatePeriodMillis=\"86400000\"\n\tandroid:widgetCategory=\"keyguard|home_screen\">\n\n</appwidget-provider>"
  },
  {
    "path": "app/src/main/res/xml/searchable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<!--\tTODO android:searchSuggestIntentData=\"content://com.nononsenseapps.NotePad/task\"\n \t    needs to be there, but it's not clear what it does. \"com.nononsenseapps.NotePad\" used\n \t    to be the android:searchSuggestAuthority, but it seems to work anyway ? -->\n<searchable xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:hint=\"@string/search_hint\"\n\tandroid:includeInGlobalSearch=\"true\"\n\tandroid:label=\"@string/app_name_short\"\n\tandroid:searchSettingsDescription=\"@string/search_description\"\n\tandroid:searchSuggestAuthority=\"@string/NnnAuthority_1\"\n\tandroid:searchSuggestIntentAction=\"android.intent.action.VIEW\"\n\tandroid:searchSuggestIntentData=\"content://com.nononsenseapps.NotePad/task\"\n\tandroid:searchSuggestSelection=\"dummyline\"\n\tandroid:voiceSearchMode=\"showVoiceSearchButton|launchRecognizer\">\n</searchable>"
  },
  {
    "path": "app/src/main/res/xml/searchabledeleted.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<searchable xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:hint=\"@string/search_hint\"\n\tandroid:label=\"@string/app_name_short\"\n\tandroid:voiceSearchMode=\"showVoiceSearchButton|launchRecognizer\">\n</searchable>"
  },
  {
    "path": "build.gradle",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n\n// Top-level build file where you can add configuration options common to all sub-modules:\n// everything in here will be applied to ALL gradle modules\nbuildscript {\n    repositories {\n        mavenCentral()\n        google()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.11.1'\n        // no kotlin: this is an old project, it's easier to keep java\n        // classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20\"\n    }\n}\n\nallprojects {\n    repositories {\n        mavenCentral()\n        google()\n    }\n    tasks.withType(JavaCompile).tap {\n        configureEach {\n            options.incremental = true\n            options.encoding = 'UTF-8'\n        }\n    }\n}\n\n// import our custom gradle tasks\napply from: 'customTasks.gradle'"
  },
  {
    "path": "contract/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    namespace = \"com.nononsenseapps.notepad.providercontract\" // for R.*\n    compileSdk = 36\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 36\n        versionCode 1\n        versionName \"1.0.0\"\n    }\n\n    lint {\n        htmlReport = false\n        textReport = false\n        abortOnError = false\n        explainIssues = true\n        checkTestSources = true\n        // turn off checking the given issue id's\n        disable += ['RtlHardcoded', 'ApplySharedPref']\n    }\n\n    compileOptions {\n        // enable support for new language APIs in old devices\n        coreLibraryDesugaringEnabled = true\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n}\n\ndependencies {\n    implementation 'androidx.annotation:annotation:1.9.1'\n    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'\n}\n"
  },
  {
    "path": "contract/src/main/AndroidManifest.xml",
    "content": "<manifest>\n    <application/>\n</manifest>\n"
  },
  {
    "path": "contract/src/main/java/com/nononsenseapps/notepad/providercontract/ProviderBase.java",
    "content": "package com.nononsenseapps.notepad.providercontract;\n\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_ACCOUNTS;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_ACCOUNTS_ID;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_FEATURES;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_ITEM;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_ITEM_ID;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_ITEM_ID_MOVE;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_SUBITEM;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_SUBITEM_ID;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.URI_SUBITEM_ID_MOVE;\nimport static com.nononsenseapps.notepad.providercontract.UriContract.addUrisToMatcher;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.net.Uri;\n\nimport androidx.annotation.NonNull;\n\n/**\n * A base class for any provider that wishes to implement the contract to NoNonsense Notes.\n * It is offered as a convenience only, and is not required. You are free to write your own\n * provider from scratch/modify your existing/this one.\n */\npublic abstract class ProviderBase extends ContentProvider {\n\n\tprotected final UriMatcher uriMatcher;\n\n\tprotected abstract String getAuthority();\n\n\tpublic ProviderBase() {\n\t\tsuper();\n\n\t\turiMatcher = new UriMatcher(UriMatcher.NO_MATCH);\n\t\taddContractUrisToMatcher(uriMatcher);\n\t}\n\n\tprivate void addContractUrisToMatcher(@NonNull final UriMatcher uriMatcher) {\n\t\taddUrisToMatcher(uriMatcher, getAuthority());\n\t}\n\n\t@Override\n\tpublic Cursor query(@NonNull Uri uri, String[] projection, String selection,\n\t\t\t\t\t\tString[] selectionArgs, String sortOrder) {\n\t\tswitch (uriMatcher.match(uri)) {\n\t\t\tcase URI_FEATURES:\n\t\t\t\treturn queryFeatures(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\tcase URI_ACCOUNTS:\n\t\t\t\treturn queryAccounts(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\tcase URI_ACCOUNTS_ID:\n\t\t\t\treturn queryAccountsId(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\tcase URI_ITEM:\n\t\t\t\treturn queryItems(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\tcase URI_ITEM_ID:\n\t\t\t\treturn queryItemsId(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\tcase URI_SUBITEM:\n\t\t\t\treturn querySubItems(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\tcase URI_SUBITEM_ID:\n\t\t\t\treturn querySubItemsId(uri, projection, selection, selectionArgs, sortOrder);\n\t\t\tcase UriMatcher.NO_MATCH:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown URI: \" + uri);\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"This URI does not support query: \" + uri);\n\t\t}\n\t}\n\n\tprotected abstract Cursor querySubItemsId(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t\t\t  String[] selectionArgs, String sortOrder);\n\n\tprotected abstract Cursor querySubItems(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t\t\tString[] selectionArgs, String sortOrder);\n\n\tprotected abstract Cursor queryItemsId(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t\t   String[] selectionArgs, String sortOrder);\n\n\tprotected abstract Cursor queryItems(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t\t String[] selectionArgs, String sortOrder);\n\n\tprotected abstract Cursor queryAccountsId(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t\t\t  String[] selectionArgs, String sortOrder);\n\n\tprotected abstract Cursor queryAccounts(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t\t\tString[] selectionArgs, String sortOrder);\n\n\tprotected abstract Cursor queryFeatures(Uri uri, String[] projection, String selection,\n\t\t\t\t\t\t\t\t\t\t\tString[] selectionArgs, String sortOrder);\n\n\t@Override\n\tpublic String getType(@NonNull final Uri uri) {\n\t\tthrow new IllegalArgumentException(\"Not yet implemented\");\n\t}\n\n\t@Override\n\tpublic Uri insert(@NonNull Uri uri, ContentValues values) {\n\t\tswitch (uriMatcher.match(uri)) {\n\t\t\tcase URI_ACCOUNTS:\n\t\t\t\treturn insertAccount(uri, values);\n\t\t\tcase URI_ITEM:\n\t\t\t\treturn insertItem(uri, values);\n\t\t\tcase URI_SUBITEM:\n\t\t\t\treturn insertSubItem(uri, values);\n\t\t\tcase UriMatcher.NO_MATCH:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown URI: \" + uri);\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"This URI does not support insert: \" + uri);\n\t\t}\n\t}\n\n\tprotected abstract Uri insertSubItem(Uri uri, ContentValues values);\n\n\tprotected abstract Uri insertItem(Uri uri, ContentValues values);\n\n\tprotected abstract Uri insertAccount(Uri uri, ContentValues values);\n\n\t@Override\n\tpublic int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {\n\t\tswitch (uriMatcher.match(uri)) {\n\t\t\tcase URI_ACCOUNTS:\n\t\t\t\treturn deleteAccounts(uri, selection, selectionArgs);\n\t\t\tcase URI_ACCOUNTS_ID:\n\t\t\t\treturn deleteAccountsId(uri, selection, selectionArgs);\n\t\t\tcase URI_ITEM:\n\t\t\t\treturn deleteItem(uri, selection, selectionArgs);\n\t\t\tcase URI_ITEM_ID:\n\t\t\t\treturn deleteItemId(uri, selection, selectionArgs);\n\t\t\tcase URI_SUBITEM:\n\t\t\t\treturn deleteSubItem(uri, selection, selectionArgs);\n\t\t\tcase URI_SUBITEM_ID:\n\t\t\t\treturn deleteSubItemId(uri, selection, selectionArgs);\n\t\t\tcase UriMatcher.NO_MATCH:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown URI: \" + uri);\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"This URI does not support delete: \" + uri);\n\t\t}\n\t}\n\n\tprotected abstract int deleteSubItemId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int deleteSubItem(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int deleteItemId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int deleteItem(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int deleteAccountsId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int deleteAccounts(Uri uri, String selection, String[] selectionArgs);\n\n\t@Override\n\tpublic int update(@NonNull Uri uri, ContentValues values, String selection,\n\t\t\t\t\t  String[] selectionArgs) {\n\t\tswitch (uriMatcher.match(uri)) {\n\t\t\tcase URI_ACCOUNTS:\n\t\t\t\treturn updateAccounts(uri, selection, selectionArgs);\n\t\t\tcase URI_ACCOUNTS_ID:\n\t\t\t\treturn updateAccountsId(uri, selection, selectionArgs);\n\t\t\tcase URI_ITEM:\n\t\t\t\treturn updateItem(uri, selection, selectionArgs);\n\t\t\tcase URI_ITEM_ID:\n\t\t\t\treturn updateItemId(uri, selection, selectionArgs);\n\t\t\tcase URI_ITEM_ID_MOVE:\n\t\t\t\treturn moveItemId(uri, selection, selectionArgs);\n\t\t\tcase URI_SUBITEM:\n\t\t\t\treturn updateSubItem(uri, selection, selectionArgs);\n\t\t\tcase URI_SUBITEM_ID:\n\t\t\t\treturn updateSubItemId(uri, selection, selectionArgs);\n\t\t\tcase URI_SUBITEM_ID_MOVE:\n\t\t\t\treturn moveSubItemId(uri, selection, selectionArgs);\n\t\t\tcase UriMatcher.NO_MATCH:\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown URI: \" + uri);\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"This URI does not support update: \" + uri);\n\t\t}\n\t}\n\n\tprotected abstract int moveSubItemId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int updateSubItemId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int updateSubItem(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int moveItemId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int updateItemId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int updateItem(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int updateAccountsId(Uri uri, String selection, String[] selectionArgs);\n\n\tprotected abstract int updateAccounts(Uri uri, String selection, String[] selectionArgs);\n}\n"
  },
  {
    "path": "contract/src/main/java/com/nononsenseapps/notepad/providercontract/ProviderContract.java",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.nononsenseapps.notepad.providercontract;\n\n\nimport androidx.annotation.LongDef;\nimport androidx.annotation.StringDef;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * This class defines some of the contract which a provider must adhere to.\n */\npublic class ProviderContract {\n\n\t// This is the action which a provider must provide an intent-filter for.\n\tpublic static final String ACTION_PROVIDER = \"com.nononsenseapps.notepad.PROVIDER\";\n\n\t// These are the columns a provider is expected to supply.\n\t// Non-null, the uri to this item relative to the root, for example: /foo/bar\n\tpublic static final String COLUMN_PATH = \"uri\";\n\t// Non-null\n\tpublic static final String COLUMN_TYPEMASK = \"typemask\";\n\t// Non-null\n\tpublic static final String COLUMN_TITLE = \"title\";\n\t// Nullable\n\tpublic static final String COLUMN_DESCRIPTION = \"description\";\n\t// Nullable\n\tpublic static final String COLUMN_STATUS = \"status\";\n\t// Nullable\n\tpublic static final String COLUMN_DUE = \"due\";\n\t// Non-null\n\tpublic static final String COLUMN_DELETED = \"deleted\";\n\t// Projection used by the main list\n\tpublic static final String[] sMainListProjection = new String[] { COLUMN_PATH,\n\t\t\tCOLUMN_TYPEMASK, COLUMN_TITLE, COLUMN_DESCRIPTION, COLUMN_STATUS, COLUMN_DUE };\n\n\t// Bitmasks for use with typemask\n\t// Item has data (id, title, etc).\n\tpublic static final long TYPE_DATA = 0x1;\n\t// Item potentially contains sub-items. Note that either folder or data (or both) must be 1.\n\tpublic static final long TYPE_FOLDER = 0x10;\n\t// Item has a status associated with it (to-do, done, waiting, etc).\n\tpublic static final long TYPE_STATUS = 0x100;\n\t// Item supports due date.\n\tpublic static final long TYPE_DUE_DATE = 0x1000;\n\t// Item supports due time, in addition to due date.\n\tpublic static final long TYPE_DUE_TIME = 0x10000;\n\t// Item supports reminder date & time.\n\tpublic static final long TYPE_REMINDER = 0x100000;\n\t// Item supports description/content (otherwise only title).\n\tpublic static final long TYPE_DESCRIPTION = 0x1000000;\n\tpublic static final String QUERY_MOVE_PREVIOUS = \"move_previous\";\n\tpublic static final String QUERY_MOVE_PARENT = \"move_parent\";\n\n\t/**\n\t * Convenience method to OR together a bunch of bitmasks.\n\t *\n\t * @param bitvalues to combine\n\t * @return a bitmask\n\t */\n\tpublic static long getTypeMask(@TypeMask long... bitvalues) {\n\t\tlong bitmask = 0x0;\n\t\tfor (long bitvalue : bitvalues) {\n\t\t\tbitmask |= bitvalue;\n\t\t}\n\t\treturn bitmask;\n\t}\n\n\t/**\n\t * Convenience method to check if a bitmask contains a certain bitvalue.\n\t *\n\t * @param bitmask to check\n\t * @param type    typemask bit to check\n\t * @return true if bitmask has the bit set to 1, else false\n\t */\n\tpublic static boolean isType(long bitmask, @TypeMask long type) {\n\t\treturn 0 < (bitmask & type);\n\t}\n\n\t@Retention(RetentionPolicy.SOURCE)\n\t@StringDef({\n\t\t\tCOLUMN_PATH,\n\t\t\tCOLUMN_TYPEMASK,\n\t\t\tCOLUMN_TITLE,\n\t\t\tCOLUMN_DESCRIPTION,\n\t\t\tCOLUMN_STATUS,\n\t\t\tCOLUMN_DUE,\n\t\t\tCOLUMN_DELETED\n\t})\n\tpublic @interface ColumnName {\n\t}\n\n\t@Retention(RetentionPolicy.SOURCE)\n\t@LongDef({\n\t\t\tTYPE_DATA,\n\t\t\tTYPE_FOLDER,\n\t\t\tTYPE_STATUS,\n\t\t\tTYPE_DUE_DATE,\n\t\t\tTYPE_DUE_TIME,\n\t\t\tTYPE_REMINDER,\n\t\t\tTYPE_DESCRIPTION\n\t})\n\tpublic @interface TypeMask {\n\t}\n}\n"
  },
  {
    "path": "contract/src/main/java/com/nononsenseapps/notepad/providercontract/UriContract.java",
    "content": "package com.nononsenseapps.notepad.providercontract;\n\nimport android.content.UriMatcher;\n\nimport androidx.annotation.NonNull;\n\n/**\n * The URIs a ContentProvider is supposed to understand. Not all are required.\n */\npublic class UriContract {\n\n    public static final int URI_FEATURES = 1000;\n\n    public static final int URI_ACCOUNTS = 1001;\n    public static final int URI_ACCOUNTS_ID = 1002;\n\n    public static final int URI_ITEM = 2001;\n    public static final int URI_ITEM_ID = 2002;\n    public static final int URI_ITEM_ID_MOVE = 2003;\n\n    public static final int URI_SUBITEM = 3001;\n    public static final int URI_SUBITEM_ID = 3002;\n    public static final int URI_SUBITEM_ID_MOVE = 3003;\n\n    public static void addUrisToMatcher(@NonNull final UriMatcher uriMatcher,\n                                        @NonNull final String authority) {\n        uriMatcher.addURI(authority, \"features\", URI_FEATURES);\n        uriMatcher.addURI(authority, \"accounts\", URI_ACCOUNTS);\n        uriMatcher.addURI(authority, \"accounts/*\", URI_ACCOUNTS_ID);\n        uriMatcher.addURI(authority, \"accounts/*/items\", URI_ITEM);\n        uriMatcher.addURI(authority, \"accounts/*/items/*\", URI_ITEM_ID);\n        uriMatcher.addURI(authority, \"accounts/*/items/*/move\", URI_ITEM_ID_MOVE);\n        uriMatcher.addURI(authority, \"accounts/*/items/*/subitems\", URI_SUBITEM);\n        uriMatcher.addURI(authority, \"accounts/*/items/*/subitems/*\", URI_SUBITEM_ID);\n        uriMatcher.addURI(authority, \"accounts/*/items/*/subitems/*/move\", URI_SUBITEM_ID_MOVE);\n    }\n}\n"
  },
  {
    "path": "customTasks.gradle",
    "content": "// custom tasks for gradle\n\n// run with: ./gradlew checkLanguages\ntasks.register('checkLanguages') {\n\n    // TODO Feeder has a better version of this, but you need to change the locales code. See:\n    //  https://gitlab.com/spacecowboy/Feeder/-/blob/master/app/build.gradle.kts#L344\n\n    group = 'NoNonsenseNotes custom'\n    description = 'Ensure all languages in res/values-*/strings.xml are selectable in the spinner'\n\n    var readFromFolders = () -> {\n        // Read res/values/ folders, add their codes to localesInFolders\n        File resDir = new File(projectDir, \"app/src/main/res\")\n        FileFilter theFilter = (file) -> {\n                return file.name.toLowerCase().startsWith(\"values\")\n                        && file.name.toLowerCase() != \"values-v26\"\n        }\n        File[] dirs = resDir.listFiles(theFilter)\n\n        var localesInFolders = new ArrayList<String>()\n        for (File valueFolder : dirs) {\n            // exclude folders like 'values-600dp' which don't represent languages\n            if (valueFolder.name.contains(\"0dp\")) continue\n            if (valueFolder.name.contains(\"values-land\")) continue\n\n            String localeCode = valueFolder.name.replace(\"values-\", \"\")\n            localesInFolders.add(localeCode)\n        }\n        return localesInFolders\n    }\n\n    var readFromXML = () -> {\n        // read the language list in arrays.xml\n        File arrFile = new File(projectDir, \"app/src/main/res/values/arrays.xml\")\n        var reader = new FileReader(arrFile.absolutePath)\n        String xmlText = reader.text\n        reader.close()\n\n        // locate where the \"translated_langs\" array is\n        int posBegin = xmlText.indexOf(\"<string-array name=\\\"translated_langs\\\" tools:ignore=\\\"MissingTranslation\\\">\")\n        int posEnd = xmlText.indexOf(\"</string-array>\", posBegin)\n        String onlyLangArray = xmlText.substring(posBegin, posEnd)\n\n        // add locale codes to localesInXML\n        var localesInXML = new ArrayList<String>()\n        for (String line : onlyLangArray.split('\\n')) {\n            if (!line.contains(\"<item>\")) continue\n\n            String localeCode = line.replace(\"<item>\", \"\").replace(\"</item>\", \"\")\n            localesInXML.add(localeCode)\n        }\n\n        return localesInXML\n    }\n\n    doLast {\n        // actually run the task\n        var listDirSize = readFromFolders().size()\n        var listXMLSize = readFromXML().size()\n        if (listDirSize != listXMLSize) {\n            String errMsg = \"Outdated translation: XML element has \" + listXMLSize\n            errMsg += \" locales, but the values-* folders are \" + listDirSize\n            throw new GradleException(errMsg)\n        }\n    }\n}\n\n// run with: ./gradlew checkFastlane\ntasks.register('checkFastlane') {\n\n    // in fastlane/metadata/android there are folders for each language supported\n    // by F-Droid and the Play Store. Each of these folders MUST have (at least)\n    // 3 files: full_description.txt, short_description.txt, title.txt\n    // Otherwise, fastlane will crash and we can't publish the update to the app\n    // stores. This function checks if the 'fastlane/' sub-directories are\n    // configured correctly\n\n    group = 'NoNonsenseNotes custom'\n    description = 'Ensure all languages in fastlane/metadata/android folders are configured correctly'\n\n    doLast {\n        // folder containing all languages sub-directories\n        File flDir = new File(projectDir, \"fastlane/metadata/android\")\n\n        for (File langSubDir : flDir.listFiles()) {\n\n            // the Google Play Store only accepts some languages for the store description.\n            // therefore some locales cannot exist in fastlane, or it will crash.\n            // Venetian (vec) can exist, even though it is not a valid Play Store language.\n            // But Corsican (co) can not exist. Here we fail the check if invalid languages\n            // are used in fastlane folders.\n            String[] forbiddenPlayStoreLocales = [ 'co' ]\n            String localeBeingTested = langSubDir.getName()\n            if (Arrays.asList(forbiddenPlayStoreLocales).contains(localeBeingTested)) {\n                throw new GradleException(localeBeingTested+\" is not accepted by fastlane\")\n            }\n\n            // check that each sub-folder has the 3 mandatory files\n            var onlyTxtFilesFilter = new FilenameFilter() {\n                @Override\n                boolean accept(File dir, String fname) {\n                    return fname.endsWithIgnoreCase('.txt')\n                }\n            }\n\n            String[] requiredFileNames = [ 'full_description.txt', 'short_description.txt', 'title.txt' ]\n            String[] presentFileName = langSubDir.list(onlyTxtFilesFilter)\n\n            for (String fn : requiredFileNames) {\n                if (!presentFileName.contains(fn)) {\n                    // 1 of the 3 required files is missing => fail the task\n                    String errMsg = \"Missing file \" + fn + \" in folder \" + langSubDir.name + \" for fastlane\"\n                    throw new GradleException(errMsg)\n                }\n\n            }\n        }\n        // finished: we have confirmed that all the folders have the 3 required .txt files,\n        // and now fastlane can publish without issues\n    }\n}"
  },
  {
    "path": "deploy_playstore.sh",
    "content": "#!/bin/bash -eu\n\nLATEST_TAG=\"$(git describe --tags \"$(git rev-list --tags --max-count=1)\")\"\nCURRENT_VERSION=\"$(git describe --tags)\"\n\nif [ -n \"${SERVICEACCOUNTJSON:-}\" ]; then\n  cat > serviceaccount.b64 <<EOF\n${SERVICEACCOUNTJSON}\nEOF\nfi\n\nbase64 --ignore-garbage --decode serviceaccount.b64 > serviceaccount.json\n\nsed -i \"s|/path/to/serviceaccount.json|$(pwd)/serviceaccount.json|\" fastlane/Appfile\n\nif [ -n \"${KEYSTORE:-}\" ]; then\n  cat > keystore.b64 <<EOF\n${KEYSTORE}\nEOF\n\n  base64 --ignore-garbage --decode keystore.b64 > keystore\n\n  cat >> gradle.properties <<EOF\nSTORE_FILE=$(pwd)/keystore\nSTORE_PASSWORD=${KEYSTOREPASSWORD}\nKEY_ALIAS=${KEYALIAS}\nKEY_PASSWORD=${KEYSTOREPASSWORD}\nEOF\n\nfi\n\n# Delete unsupported google play store languages\nrm -rf fastlane/metadata/android/bs-BA \\\n   fastlane/metadata/android/eo \\\n   fastlane/metadata/android/tok \\\n   app/src/main/res/values-tok \\\n   fastlane/metadata/android/gl \\\n   app/src/main/res/values-gl \\\n   fastlane/metadata/android/eu \\\n   app/src/main/res/values-eu \\\n   fastlane/metadata/android/vec \\\n   app/src/main/res/values-vec \\\n   fastlane/metadata/android/he \\\n   app/src/main/res/values-he \\\n   fastlane/metadata/android/is \\\n   app/src/main/res/values-is\n\n# to check if gradle actually receives the properties\ncat gradle.properties\n\nif [[ \"${1:-}\" == \"--dry-run\" ]] && [[ \"${LATEST_TAG}\" == \"${CURRENT_VERSION}\" ]]; then\n  echo \"${CURRENT_VERSION} is a tag but --dry-run was specified - not doing anything\"\nelif [[ \"${1:-}\" == \"--dry-run\" ]] || [[ \"${LATEST_TAG}\" != \"${CURRENT_VERSION}\" ]]; then\n  echo \"${CURRENT_VERSION} is not tag - validating deployment\"\n  if [[ \"${CURRENT_VERSION}\" =~ ^[0-9.]*$ ]]; then\n    echo \"${CURRENT_VERSION} is a production release\"\n    fastlane build_and_validate track:production\n  else\n    echo \"${CURRENT_VERSION} is a beta release\"\n    # We don't have a beta track\n    # fastlane build_and_validate track:beta\n    fastlane build_and_validate track:production\n  fi\nelse\n  echo \"${CURRENT_VERSION} is a tag - deploying to store!\"\n  if [[ \"${CURRENT_VERSION}\" =~ ^[0-9.]*$ ]]; then\n    echo \"${CURRENT_VERSION} is a production release\"\n    fastlane build_and_deploy track:production\n  else\n    echo \"${CURRENT_VERSION} is a beta release\"\n    # We don't have a beta track\n    # fastlane build_and_deploy track:beta\n    fastlane build_and_validate track:production\n  fi\nfi\n\ngit checkout app fastlane gradle.properties\n"
  },
  {
    "path": "documents/CONTRIBUTING.md",
    "content": "# How to contribute\n\nThank you for investing your time in contributing to this project.\nHere you will get an overview of the contribution workflow.\n\n## Contacting the developers\n\nDevelopers are busy: their attention is a privilege, not a right.\nWe request that you value our time and don't waste it on inane matters.\nThat said, we are open to help anyone who wants to contribute, \nor simply to point out a bug. See below.\n\n## Issues\n\nEvery discussion on the project happens on [the issues page](https://github.com/spacecowboy/NotePad/issues).\nWe don't use other platforms to discuss this app's development. \nLet's centralize everything there and make it easier for the maintainers to keep up. \nWhen creating a new issue, please use the templates and provide enough information.\n\n## Translations\n\nUse [weblate](https://hosted.weblate.org/engage/no-nonsense-notes/) to help us translate the app\n\n## Testing\n\nWe use AndroidX and Espresso for android tests.\nYou can find the tests [here](../app/src/androidTest/java).\nIf you want to add tests, please separate them: those that use espresso should go\nin their specific directory, separated from the others.\nThere is no special requirement for tests, just try to follow the code style you see in other test files.\n\n## Submitting changes\n\nPlease send a GitHub Pull Request with a simple list of what you've done. \nRemember to follow our coding conventions (see below). \nAlso, make sure all of your commits are atomic: only one feature per commit.\nThis project is not under very active development, so you may have to wait for a while before we answer.\n\n## Coding conventions\n\nWe basically use the default Android Studio settings.\nBefore contributing, please run the Reformat Code tool (CTRL+ALT+L) on the `java` and `res` folders.\nWrite your code in english.\nAvoid introducing new library dependencies (but AndroidX modules are fine).\nIn particular, avoid non-free libraries like Google Services or Dropbox.\nWrite your code in Java 11, not Kotlin: we value coherence across the codebase.\n\n## What you can do\n\nWe have a list of tasks [here](https://github.com/spacecowboy/NotePad/issues/387).\nMany of these are simple, so you can just pick one and work on it. Start from the top of the first post.\nOr you can choose one of the open issues, preferably without the _secondary_ tag, but those may be harder to solve.\nYou **must tell us** that you started working on it. We could even help you get started!\n"
  },
  {
    "path": "documents/FAQ.md",
    "content": "# FAQ\n\n**Q: Why is a task manager named \"Notes\"?**\n\nA: The app started with only the note taking features. Syncing with Google Tasks seemed like a good idea. Then it only made sense to support the rest of the features of Google Tasks. Sadly, due to limitations from Google, this feature is not available anymore.\n\n**Q: Can you make a theme with yellow background and black lines?**\n\nA: Absolutely not. I designed the app to get away from things like that. If you want skeuomorphic design, go buy an iPhone.\n\n**Q: Will you add colors/priority to notes?**\n\nA: Short answer: no. Long answer: I try hard to keep the app simple. Adding more features like this would make it more complicated. There are plenty of task managers out there that have more features than my app. I don't use those features and hence I don't want them in my app. Until I see a real use for them, they won't be implemented.\n\n**Q: I want to have a check list inside a note. Can you add this? Task program X has it**\n\nA: No. That would go against the entire structure of the app. If you want a shopping list for example, create a list called \"Shopping list\". Then you have a checklist for your shopping. Don't be afraid to create lists, and then delete them when you're done with them.\n\n**Q: Will I ever be able to set sub tasks?**\n\nA: No. At this point you should look into a more complicated app, like orgzly.\n\n**Q: Can you add a save button?**\n\nA: It's a useless button. Notes are saved when they are closed or when the screen goes dark. Learn to trust it.\n\n**Q: what files can I save?**\n\nThe app can save 2 kinds of files:\n\n* ORG files, used for SD card synchronization. \n  * They are saved in a subfolder of `/storage/emulated/0/Android/data/`\n  * they will be visible to your file manager app, if you want to manually edit them\n* JSON files, for the backup-restore functionality\n  * you can save these wherever you want\n\nIf you are not satisfied, [open an issue](https://github.com/spacecowboy/NotePad/issues/new/choose) and recommend a behavior.\n\n**Q: Why are the files saved there?**\n\nA: Newer Android versions limit what we can do when working with files. See [the discussion](https://github.com/spacecowboy/NotePad/issues/454)\n\n**Q: I opened a pull request or an issue before 2022. Why did you close it?**\n\nA: The project was restarted in 2022, and many things changed. Plans for a minimal \n6.0.0 version were dropped, and the current 7.0.0 version inherits directly from 5.7.1,\nwhich was last released in 2015. Many things changed in those 7 years, so we will take\ncare of new issues and pull requests as they appear. Apologies for the inconvenience.\n\n**Q: what do you do with my private data?**\n\nA: Nothing. See the [privacy policy](./PRIVACY.md).\n\n**Q: I want to help the developers. What do I do ?**\n\nA: Read the [contributions guideline](./CONTRIBUTING.md) and proceed from there.\n\n**Q: Where did Google Tasks synchronization go?**\n\nAs [Discussed here](https://github.com/spacecowboy/NotePad/issues/426), it had to be removed.\nIf you want to recommend another sync service, simply [open an issue](https://github.com/spacecowboy/NotePad/issues/new/choose)\n"
  },
  {
    "path": "documents/PRIVACY.md",
    "content": "# No Nonsense Notes privacy policy\n\nYour privacy is your own.\nThe maintainers of this app retain NO rights to do shit with any of your data.\n\nThe app does not request the permission to go online,\nnor to access any personal account information,\nnor does it go online for any reason whatsoever.\n\nThere may still be old, leftover code that uses network functionality,\nbut it is not used and if you find it, we will gladly remove it.\n"
  },
  {
    "path": "documents/TUTORIAL.md",
    "content": "# No Nonsense Notes app tutorial\n\nOver time, this app developed to be its own thing, so it can feel disorienting for new users. \n\nThis document will explain every unintuitive feature of the app, to turn you into a pro in no time!\nThis guide is written for the phone layout. The tablet layout is a bit different.\n\n## Summary\n\n```mermaid\ngraph TD\n    A[Main view] -->|swipe right| B(Drawer menu)\n    B --> C{Choose action}\n    C -->|1 of 3 lists on top| D[fa:fa-list set an abstract view]\n    C -->|long press a list| E[edit dialog for note lists]\n    C -->|press a list| F[show list]\n    C -->|fa:fa-folder folder button| G[add a new list]\n\n    A --> H[fa:fa-dots overflow menu]\n    H --> |view archive| I[fa:fa-archive see a list of all deleted notes]\n    H --> |clear completed| J[send completed notes to archive]\n\n    A --> |tap + | K[note detail page]\n    A --> |tap a note | K\n    K --> |use the back button or gesture| L[the note is automatically saved]\n    L --> A\n    K --> |overflow menu| M[note history]\n\n    M --> |drag the line| N[see an old version of the note]\n    N --> |press DONE| O[restore the old version of the note]\n    O --> K\n\n    K --> |press the checkbox| P[mark the note as completed, for your own convenience]\n    K --> |press the dropdown menu| Q[choose a due date]\n    Q --> |X button| R[remove the due date]\n\n    K --> |add a reminder| S[a new panel appears, to configure the reminder: date, hour, repeating week days and X to delete it]\n\n    \n\n%% comment: you should finish this chart\n%% https://mermaid.live/ is very helpful\n\n```\n\n## Navigating the app\n\n### The main view\n\n![main view](../tutorial/tut1.png)\n\nHere you see:\n\n* 3 note lists (Next 5 days, Agenda, Ideas): you can swipe left and right to navigate between them\n* 2 tasks, ordered by due date: the 2° one is marked as \"completed\"\n* the due date of each task, in the corner on the right\n    * you can choose how it is displayed\n* a `+` button to create a new note\n* a search button to find and open one of your notes\n    * it supports voice search\n    * if you type, you get suggestions. click on one to open that note\n* the hamburger menu `☰` which can open the drawer menu on the left\n* the overflow menu `⋮` which has many options\n    * \"Sync\" forces an update of the files on the SD card\n    * archive opens the \"deleted tasks page\", also called \"note archive\"\n    * \"clear completed\" can delete all tasks marked as completed from the current list only\n    * \"settings\" opens the app preferences page\n\n### The left drawer\n\n![left drawer](../tutorial/tut2.png)\n\nHere you see:\n\n* the `📁+` icon on the action bar: press it to create a new list\n* the \"tasks\" section, which has 3 views that you can use to quickly see some of your notes\n    * For example, click \"today\" to see tasks due today\n    * hold your finger on one of those 3 items to select it as default view\n        * it will be always on the left of every other list\n        * in the first image you saw \"next 5 days\" as the default view\n* the \"lists\" section has all the note lists you created\n    * Since \"agenda\" has 4 un-checked (incomplete) tasks, you see \"4\" next to it\n    * hold your finger on one of those lists to open a popup to edit it\n\n### The list edit dialog\n\n![edit dialog](../tutorial/tut3.png)\n\nIn this dialog you can:\n\n* choose a name for the list\n    * They are sorted alphabetically\n    * If you want it to appear first, put a `_`, like \"_zebra\"\n    * I name my lists `1 Ideas`, `2 Useless` and `3 Agenda` to get a custom order\n* set one list as default\n    * the app will open on that list\n    * links you share to this app from your web browser will go into this list\n* choose how notes are ordered on this list in particular\n    * \"latest updated\" shows first the notes that you edited more recently\n    * \"due date\" also groups the notes by week, month and year\n    * \"manual\" lets you drag and reorder the notes\n* choose how to show the list items\n    * \"checkable tasks\" have a check-box and a due date\n    * \"simple notes\" don't, but they can still have reminders and notifications\n\n### The archive view\n\n![archive](../tutorial/tut4.png)\n\nHere you can:\n\n* see a list of notes you deleted. Notes with darker text were completed before being deleted\n* select one or more notes by touching them\n* select them all with the icon next to the trash bin\n* delete the selected notes with the trash bin icon\n* restore the selected notes by pressing \"restore\"\n* search among the deleted notes with the provided search bar\n\n### The settings page\n\n![settings](../tutorial/tut5.png)\n\nHere you can see all our settings. They are laid out linearly. I recommend checking all of them, but\nin particular the **notification settings**, where you can customize the behavior of notifications\nand bypass some android design flaws to **make reminders appear more reliably**. \n\nThe **backup** settings are also useful: you can save the notes as a json file, and restore them if you\nre-install the app even on another phone.\n\n### The task detail panel\n\n![task detail view](../tutorial/tut6.png)\n\nHere you can:\n\n* edit the task text\n    * the 1° line is the title, the rest is the content of the note\n    * links and phone numbers can be highlighted and clicked to open them\n* set the note as \"done\" with the check-box\n* set a due date for the note. This note is due \"Thursday, 16 march\"\n    * you can choose how this date is displayed: go see the settings\n* remove the due date with the `X` on its right\n* add as many reminders as you want\n* choose the date and time of each reminder, separately.\n    * in this case, the reminder will show a notification on \"October 5\" at \"11:59 PM\"\n* remove the reminder with the `X` on its right\n* choose on which week days the reminder will repeat\n    * touch a week day to select it: you see the colored underline.\n    * Now when you dismiss the reminder, it will be rescheduled in the chosen day, at the same hour\n    * Very useful for creating weekly routines: make things automatic!\n* interact with the action bar on top\n    * the `<-` arrow **saves** the note and brings you back to the list\n    * the `+` button saves this note and reopens this page to let you write a new note\n    * the trash bin icon deletes this note, sending it to the \"archive\"\n    * then, in the `⋮` menu, you have\n        * \"cancel\", which brings you back to the menu but **discards** your edits\n        * \"share\", to send the title & content of this note to another app, as plain text\n        * \"lock note\", to apply a password in order to protect your note\n        * \"time machine\", which shows you the \"task history\" page\n\n### The task history page\n\n![task history](../tutorial/tut7.png)\n\nHere you can:\n\n* drag the indicator to see all previous version of the note (but only the ones that you saved)\n* press \"cancel\" to undo and go back\n* find a version you like, press \"done\", and overwrite the current note with that content\n\n## App features\n\nNavigating the app will show you (almost) all it has to offer. However, there are some features that\nstill need to be explained.\n\n### Passwords\n\nYou can edit the password in the settings, and apply it to single notes. Passwords protect every\naspect of the note. Unless the user provides the password:\n\n* only the note's title is visibile, not the content\n* the note can't be deleted\n* its reminders can't be edited. The notification still appear, don't worry\n* ~~backups and restores will be blocked~~ (still working on this one...)\n\n### SD sync\n\nYour notes are saved internally by the app, and you can get an updated representation of the notes\nin the form of org files (=plain text) if you enable \"sd sync\" under the \"sync\" settings. Then you\ncan sync those files and use this app together with emacs in org mode, for example. Changes go both\nways: edit the org files with a text editor to update notes in the app. Sync is automatic, but you\ncan force it by pressing \"Sync\" from the menu next to the search bar. The app will produce 1 org\nfile for each list. Deleting an org file deletes the equivalent list in the app, and adding a file\ncreates a new list with the same name and all the notes it contains.\n\nFor example, if your notes look like this:\n\n![org notes](../tutorial/tut10.png)\n\nThen in `/storage/emulated/0/Android/data/com.nononsenseapps.notepad/files/orgfiles/Tasks.org`, or\nsome equivalent path, you will have a file with this text:\n\n```org\n\n* TODO Note 1\n# NONSENSEID: 7EEEE94E\nDEADLINE: <2023-01-09 Mon>\nSecond row\n\n* DONE Note 2\n# NONSENSEID: 4535371F\n\nContent row\n\n* TODO Note 3\n# NONSENSEID: 58671F49\n\n```\n\nLimitations:\n\n* the reminders you set in the app cannot be saved to the org file\n    * In this case, \"Note 3\" had a reminder, but you don't see it in the equivalent org file\n* Archived notes, which you can see in the \"archive\" page, will **not** be saved to an org file\n* The note history is not saved: only the last version of the note goes into the org file\n* Read-only org files are officially unsupported. Import only writable files: \"rw\" and not \"r\"\n\n### Links\n\nThis app is a decent bookmark manager. You can save links by sharing them from the browser, and you\ncan click them from the list-view or the note-detail-view to reopen them. Other apps let you open\nonly one link at a time, overwriting the previous one, but in this app you can open as many links as\nyou want. Go into the settings to disable auto-highlighting for links, if you feel that you click\nthem by mistake and find it irritating.\n\n### Backups\n\nSince this app can export all the notes as a json file, you can elaborate them on a computer.\n\nThe following is a short powershell script that outputs the content of a list as a plain text file with a list of links:\n\n```powershell\n$json = Get-Content D:\\Desktop\\NoNonsenseNotes_Backup.json | ConvertFrom-Json \n$x = $json.lists | Where-Object { $_.title -eq 'My list name' }\n$x.tasks | Sort-Object -Property note `\n  | ForEach-Object { [string]::Format(\"* [{0}]({1})\", $_.title, $_.note) } `\n  > D:\\Desktop\\links.md \nRead-Host \"Done. Press ENTER to continue\"\n```\n\nvery useful if don't want to sync bookmarks with google chrome.\n\n### Reminders\n\nOn each task (or note) you can add as many reminders as you want. Then you configure them, choosing\non which day and hour you want to see each reminder. They appear as notifications. If you set the\nreminder to repeat on some week days, and when the notification appears you swipe it away, the\nreminder will get rescheduled to the next valid day.\n\n![notification](../tutorial/tut8.png)\n\nAs you just saw, in some occasions the notification can show 2 action buttons:\n\n* \"snooze\" will set a new reminder **after 30 minutes** from the moment you press _snooze_\n* \"completed\" sets the task as completed in the app:\n    * its check-box will be checked\n    * all of its reminders will be deleted\n\n_Repeating reminders_, those reminders configured to repeat on at least one week day, don't have\nthe \"completed\" button, by design. They have the \"snooze\" button. It works by rescheduling the\nreminder to the next applicable day and adding a new reminder, **after 30 minutes** from the moment\nyou press _snooze_\n\n### Performance\n\nThe notification settings, which you reach from the `⋮` menu -> then \"settings\"\n-> then \"notifications\", can bring you to pages where you disable hibernation and battery\noptimization.\n\n![prefs](../tutorial/tut9.png)\n\nI recommend you do this, and also enable \"exact notifications\", because then the reminders from this\napp will show up more reliably, even when the phone is sleeping. I assume that, if you set a\nreminder at 3:00 AM, and the phone is in \"standby\" mode, you actually\n**want to see the reminder**, and with these system settings you tell Android to deliver the\nnotifications on time instead of saving battery, at least for this app.\n\n## Widgets\n\nThis app has 2 widgets. They look like this:\n\n![home screen](../tutorial/tut11.png)\n\n### The shortcut\n\nWith the shortcut widget, you can quickly open a list from the home screen. You add it to the home\nscreen as a normal app widget. When you do, a settings page appears. There, a toggle-button allows\nyou to configure the shortcut to create a new note in the selected list, instead of opening it\n\n### The list widget\n\nThe previous picture shows how the \"list widget\" looks like on the home screen. To add the widget,\nlook for this icon among all available widgets:\n\n![list widget icon](../app/src/main/res/drawable-nodpi/preview_ori_portrait.png)\n\nThen you will see a page where you can customize the widget's appearance:\n\n![widget prefs](../tutorial/tut12.png)\n\nHere you can choose:\n\n* which list of notes should be shown on the widget\n* how to order the notes of that list\n* the color for the widget, either black or white\n* a level of transparency for the background\n* the amount of rows to show before cutting a note\n* if the check-boxes to complete notes should be hidden in the widget\n* if the due date of notes should be shown\n* if the widget header (the row on top) should be shown.\n    * if you hide it, you will have to re-create the widget in order to change its settings\n\n#### Show a single note\n\nYou can configure the list widget to show a single note. It will look like this:\n\n![single](../tutorial/tut13.png)\n\nYou have to:\n\n* make a new list with only 1 note\n* add a new list widget from this app to your home screen. Its configuration page will open\n* in the first drop-down menu, select the list you just made\n* confirm \"manual\" sorting on the second drop-down menu\n* check the \"hide checkbox\" option\n* check the \"hide entire widget header\" option\n* press \"DONE\" to confirm\n\nWith that you have the minimal view shown in the picture. You can click the note to open a page to\nedit it. To re-configure the widget, you will have to delete it and make a new one.\n\n## Emoji tags 🏷️\n\nYou can use emojis as tags. Use the emoji panel on your keyboard app to write an emoji on a note.\nWhen you search the emoji, only notes that contain it will appear in the results.\nYou have complete controls on what tags you use for finance 💵, purchases 🛒, medical ⚕️, ideas 💭, and so on.\nThese tags are easy to add, remove and combine as you see fit.\n\n## Learn more\n\n* see the [FAQ](./FAQ.md) which is mostly about design choices\n* see how you can [help with the development](./CONTRIBUTING.md)\n* ask your question by [opening an issue](https://github.com/spacecowboy/NotePad/issues/new/choose) \n"
  },
  {
    "path": "external/drag-sort-listview/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:versionCode=\"4\"\n\tandroid:versionName=\"0.6.1\">\n</manifest> \n"
  },
  {
    "path": "external/drag-sort-listview/build.gradle",
    "content": "/*\n * Copyright (c) 2014 Jonas Kalderstam.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'com.android.library'\n\n// suppress (most) warnings related to javadocs that we don't care about\ntasks.withType(Javadoc).configureEach {\n    options.addStringOption('Xdoclint:none', '-quiet')\n}\n\nandroid {\n    compileSdk = 36\n    namespace = \"com.mobeta.android.dslv\"\n\n    defaultConfig {\n        targetSdk = 36\n        minSdkVersion 23\n    }\n\n    lint {\n        htmlReport = false\n        textReport = false\n        abortOnError = false\n        explainIssues = true\n        checkTestSources = true\n        // turn off checking the given issue id's\n        // disable += ['RtlHardcoded', 'MissingSuperCall']\n    }\n\n    compileOptions {\n        // enable support for new language APIs in old devices\n        coreLibraryDesugaringEnabled = true\n        sourceCompatibility = JavaVersion.VERSION_17\n        targetCompatibility = JavaVersion.VERSION_17\n    }\n\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            java.srcDirs = ['src']\n            res.srcDirs = ['res']\n            resources.srcDirs = ['src']\n        }\n\n        debug.setRoot('build-types/debug')\n        release.setRoot('build-types/release')\n    }\n\n}\n\ndependencies {\n    implementation \"androidx.cursoradapter:cursoradapter:1.0.0\"\n    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'\n}\n"
  },
  {
    "path": "external/drag-sort-listview/res/values/dslv_attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<resources>\n\t<declare-styleable name=\"DragSortListView\">\n\t\t<attr name=\"collapsed_height\" format=\"dimension\"/>\n\t\t<attr name=\"drag_scroll_start\" format=\"float\"/>\n\t\t<attr name=\"max_drag_scroll_speed\" format=\"float\"/>\n\t\t<attr name=\"float_background_color\" format=\"color\"/>\n\t\t<attr name=\"remove_mode\">\n\t\t\t<enum name=\"clickRemove\" value=\"0\"/>\n\t\t\t<enum name=\"flingRemove\" value=\"1\"/>\n\t\t</attr>\n\t\t<attr name=\"track_drag_sort\" format=\"boolean\"/>\n\t\t<attr name=\"float_alpha\" format=\"float\"/>\n\t\t<attr name=\"slide_shuffle_speed\" format=\"float\"/>\n\t\t<attr name=\"remove_animation_duration\" format=\"integer\"/>\n\t\t<attr name=\"drop_animation_duration\" format=\"integer\"/>\n\t\t<attr name=\"drag_enabled\" format=\"boolean\"/>\n\t\t<attr name=\"sort_enabled\" format=\"boolean\"/>\n\t\t<attr name=\"remove_enabled\" format=\"boolean\"/>\n\t\t<attr name=\"drag_start_mode\">\n\t\t\t<enum name=\"onDown\" value=\"0\"/>\n\t\t\t<enum name=\"onMove\" value=\"1\"/>\n\t\t\t<enum name=\"onLongPress\" value=\"2\"/>\n\t\t</attr>\n\t\t<attr name=\"drag_handle_id\" format=\"integer\"/>\n\t\t<attr name=\"fling_handle_id\" format=\"integer\"/>\n\t\t<attr name=\"click_remove_id\" format=\"integer\"/>\n\t\t<attr name=\"use_default_controller\" format=\"boolean\"/>\n\t</declare-styleable>\n</resources>\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortController.java",
    "content": "package com.mobeta.android.dslv;\n\nimport android.graphics.Point;\nimport android.view.GestureDetector;\nimport android.view.HapticFeedbackConstants;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.widget.AdapterView;\n\n/**\n * Class that starts and stops item drags on a {@link DragSortListView}\n * based on touch gestures. This class also inherits from\n * {@link SimpleFloatViewManager}, which provides basic float View\n * creation.\n *\n * An instance of this class is meant to be passed to the methods\n * {@link DragSortListView#setTouchListener()} and\n * {@link DragSortListView#setFloatViewManager()} of your\n * {@link DragSortListView} instance.\n */\npublic class DragSortController extends SimpleFloatViewManager implements View.OnTouchListener, GestureDetector.OnGestureListener {\n\n\t/**\n\t * Drag init mode enum.\n\t */\n\tpublic static final int ON_DOWN = 0;\n\tpublic static final int ON_DRAG = 1;\n\tpublic static final int ON_LONG_PRESS = 2;\n\n\tprivate int mDragInitMode = ON_DOWN;\n\n\tprivate boolean mSortEnabled = true;\n\n\t/**\n\t * Remove mode enum.\n\t */\n\tpublic static final int CLICK_REMOVE = 0;\n\tpublic static final int FLING_REMOVE = 1;\n\n\t/**\n\t * The current remove mode.\n\t */\n\tprivate int mRemoveMode;\n\n\tprivate boolean mRemoveEnabled = false;\n\tprivate boolean mIsRemoving = false;\n\n\tprivate GestureDetector mDetector;\n\n\tprivate GestureDetector mFlingRemoveDetector;\n\n\tprivate int mTouchSlop;\n\n\tpublic static final int MISS = -1;\n\n\tprivate int mHitPos = MISS;\n\tprivate int mFlingHitPos = MISS;\n\n\tprivate int mClickRemoveHitPos = MISS;\n\n\tprivate int[] mTempLoc = new int[2];\n\n\tprivate int mItemX;\n\tprivate int mItemY;\n\n\tprivate int mCurrX;\n\tprivate int mCurrY;\n\n\tprivate boolean mDragging = false;\n\n\tprivate float mFlingSpeed = 500f;\n\n\tprivate int mDragHandleId;\n\n\tprivate int mClickRemoveId;\n\n\tprivate int mFlingHandleId;\n\tprivate boolean mCanDrag;\n\n\tprivate DragSortListView mDslv;\n\tprivate int mPositionX;\n\n\t/**\n\t * Calls {@link #DragSortController(DragSortListView, int)} with a\n\t * 0 drag handle id, FLING_RIGHT_REMOVE remove mode,\n\t * and ON_DOWN drag init. By default, sorting is enabled, and\n\t * removal is disabled.\n\t *\n\t * @param dslv The DSLV instance\n\t */\n\tpublic DragSortController(DragSortListView dslv) {\n\t\tthis(dslv, 0, ON_DOWN, FLING_REMOVE);\n\t}\n\n\tpublic DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode) {\n\t\tthis(dslv, dragHandleId, dragInitMode, removeMode, 0);\n\t}\n\n\tpublic DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode, int clickRemoveId) {\n\t\tthis(dslv, dragHandleId, dragInitMode, removeMode, clickRemoveId, 0);\n\t}\n\n\t/**\n\t * By default, sorting is enabled, and removal is disabled.\n\t *\n\t * @param dslv         The DSLV instance\n\t * @param dragHandleId The resource id of the View that represents\n\t *                     the drag handle in a list item.\n\t */\n\tpublic DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode,\n\t\t\t\t\t\t\t  int removeMode, int clickRemoveId, int flingHandleId) {\n\t\tsuper(dslv);\n\t\tmDslv = dslv;\n\t\tmDetector = new GestureDetector(dslv.getContext(), this);\n\t\tmFlingRemoveDetector = new GestureDetector(dslv.getContext(), mFlingRemoveListener);\n\t\tmFlingRemoveDetector.setIsLongpressEnabled(false);\n\t\tmTouchSlop = ViewConfiguration.get(dslv.getContext()).getScaledTouchSlop();\n\t\tmDragHandleId = dragHandleId;\n\t\tmClickRemoveId = clickRemoveId;\n\t\tmFlingHandleId = flingHandleId;\n\t\tsetRemoveMode(removeMode);\n\t\tsetDragInitMode(dragInitMode);\n\t}\n\n\n\tpublic int getDragInitMode() {\n\t\treturn mDragInitMode;\n\t}\n\n\t/**\n\t * Set how a drag is initiated. Needs to be one of\n\t * {@link ON_DOWN}, {@link ON_DRAG}, or {@link ON_LONG_PRESS}.\n\t *\n\t * @param mode The drag init mode.\n\t */\n\tpublic void setDragInitMode(int mode) {\n\t\tmDragInitMode = mode;\n\t}\n\n\t/**\n\t * Enable/Disable list item sorting. Disabling is useful if only item\n\t * removal is desired. Prevents drags in the vertical direction.\n\t *\n\t * @param enabled Set <code>true</code> to enable list\n\t *                item sorting.\n\t */\n\tpublic void setSortEnabled(boolean enabled) {\n\t\tmSortEnabled = enabled;\n\t}\n\n\tpublic boolean isSortEnabled() {\n\t\treturn mSortEnabled;\n\t}\n\n\t/**\n\t * One of {@link CLICK_REMOVE}, {@link FLING_RIGHT_REMOVE},\n\t * {@link FLING_LEFT_REMOVE},\n\t * {@link SLIDE_RIGHT_REMOVE}, or {@link SLIDE_LEFT_REMOVE}.\n\t */\n\tpublic void setRemoveMode(int mode) {\n\t\tmRemoveMode = mode;\n\t}\n\n\tpublic int getRemoveMode() {\n\t\treturn mRemoveMode;\n\t}\n\n\t/**\n\t * Enable/Disable item removal without affecting remove mode.\n\t */\n\tpublic void setRemoveEnabled(boolean enabled) {\n\t\tmRemoveEnabled = enabled;\n\t}\n\n\tpublic boolean isRemoveEnabled() {\n\t\treturn mRemoveEnabled;\n\t}\n\n\t/**\n\t * Set the resource id for the View that represents the drag\n\t * handle in a list item.\n\t *\n\t * @param id An android resource id.\n\t */\n\tpublic void setDragHandleId(int id) {\n\t\tmDragHandleId = id;\n\t}\n\n\t/**\n\t * Set the resource id for the View that represents the fling\n\t * handle in a list item.\n\t *\n\t * @param id An android resource id.\n\t */\n\tpublic void setFlingHandleId(int id) {\n\t\tmFlingHandleId = id;\n\t}\n\n\t/**\n\t * Set the resource id for the View that represents click\n\t * removal button.\n\t *\n\t * @param id An android resource id.\n\t */\n\tpublic void setClickRemoveId(int id) {\n\t\tmClickRemoveId = id;\n\t}\n\n\t/**\n\t * Sets flags to restrict certain motions of the floating View\n\t * based on DragSortController settings (such as remove mode).\n\t * Starts the drag on the DragSortListView.\n\t *\n\t * @param position The list item position (includes headers).\n\t * @param deltaX   Touch x-coord minus left edge of floating View.\n\t * @param deltaY   Touch y-coord minus top edge of floating View.\n\t * @return True if drag started, false otherwise.\n\t */\n\tpublic boolean startDrag(int position, int deltaX, int deltaY) {\n\n\t\tint dragFlags = 0;\n\t\tif (mSortEnabled && !mIsRemoving) {\n\t\t\tdragFlags |= DragSortListView.DRAG_POS_Y | DragSortListView.DRAG_NEG_Y;\n\t\t}\n\t\tif (mRemoveEnabled && mIsRemoving) {\n\t\t\tdragFlags |= DragSortListView.DRAG_POS_X;\n\t\t\tdragFlags |= DragSortListView.DRAG_NEG_X;\n\t\t}\n\n\t\tmDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), dragFlags, deltaX,\n\t\t\t\tdeltaY);\n\t\treturn mDragging;\n\t}\n\n\t@Override\n\tpublic boolean onTouch(View v, MotionEvent ev) {\n\t\tif (!mDslv.isDragEnabled() || mDslv.listViewIntercepted()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmDetector.onTouchEvent(ev);\n\t\tif (mRemoveEnabled && mDragging && mRemoveMode == FLING_REMOVE) {\n\t\t\tmFlingRemoveDetector.onTouchEvent(ev);\n\t\t}\n\n\t\tint action = ev.getAction() & MotionEvent.ACTION_MASK;\n\t\tswitch (action) {\n\t\t\tcase MotionEvent.ACTION_DOWN:\n\t\t\t\tmCurrX = (int) ev.getX();\n\t\t\t\tmCurrY = (int) ev.getY();\n\t\t\t\tbreak;\n\t\t\tcase MotionEvent.ACTION_UP:\n\t\t\t\tif (mRemoveEnabled && mIsRemoving) {\n\t\t\t\t\tint x = mPositionX >= 0 ? mPositionX : -mPositionX;\n\t\t\t\t\tint removePoint = mDslv.getWidth() / 2;\n\t\t\t\t\tif (x > removePoint) {\n\t\t\t\t\t\tmDslv.stopDragWithVelocity(true, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase MotionEvent.ACTION_CANCEL:\n\t\t\t\tmIsRemoving = false;\n\t\t\t\tmDragging = false;\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Overrides to provide fading when slide removal is enabled.\n\t */\n\t@Override\n\tpublic void onDragFloatView(View floatView, Point position, Point touch) {\n\n\t\tif (mRemoveEnabled && mIsRemoving) {\n\t\t\tmPositionX = position.x;\n\t\t}\n\t}\n\n\t/**\n\t * Get the position to start dragging based on the ACTION_DOWN\n\t * MotionEvent. This function simply calls\n\t * {@link #dragHandleHitPosition(MotionEvent)}. Override\n\t * to change drag handle behavior;\n\t * this function is called internally when an ACTION_DOWN\n\t * event is detected.\n\t *\n\t * @param ev The ACTION_DOWN MotionEvent.\n\t * @return The list position to drag if a drag-init gesture is\n\t * detected; MISS if unsuccessful.\n\t */\n\tpublic int startDragPosition(MotionEvent ev) {\n\t\treturn dragHandleHitPosition(ev);\n\t}\n\n\tpublic int startFlingPosition(MotionEvent ev) {\n\t\treturn mRemoveMode == FLING_REMOVE ? flingHandleHitPosition(ev) : MISS;\n\t}\n\n\t/**\n\t * Checks for the touch of an item's drag handle (specified by\n\t * {@link #setDragHandleId(int)}), and returns that item's position\n\t * if a drag handle touch was detected.\n\t *\n\t * @param ev The ACTION_DOWN MotionEvent.\n\t * @return The list position of the item whose drag handle was\n\t * touched; MISS if unsuccessful.\n\t */\n\tpublic int dragHandleHitPosition(MotionEvent ev) {\n\t\treturn viewIdHitPosition(ev, mDragHandleId);\n\t}\n\n\tpublic int flingHandleHitPosition(MotionEvent ev) {\n\t\treturn viewIdHitPosition(ev, mFlingHandleId);\n\t}\n\n\tpublic int viewIdHitPosition(MotionEvent ev, int id) {\n\t\tfinal int x = (int) ev.getX();\n\t\tfinal int y = (int) ev.getY();\n\n\t\tint touchPos = mDslv.pointToPosition(x, y); // includes headers/footers\n\n\t\tfinal int numHeaders = mDslv.getHeaderViewsCount();\n\t\tfinal int numFooters = mDslv.getFooterViewsCount();\n\t\tfinal int count = mDslv.getCount();\n\n\t\t// Log.d(\"mobeta\", \"touch down on position \" + itemnum);\n\t\t// We're only interested if the touch was on an\n\t\t// item that's not a header or footer.\n\t\tif (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders\n\t\t\t\t&& touchPos < (count - numFooters)) {\n\t\t\tfinal View item = mDslv.getChildAt(touchPos - mDslv.getFirstVisiblePosition());\n\t\t\tfinal int rawX = (int) ev.getRawX();\n\t\t\tfinal int rawY = (int) ev.getRawY();\n\n\t\t\tView dragBox = id == 0 ? item : (View) item.findViewById(id);\n\t\t\tif (dragBox != null) {\n\t\t\t\tdragBox.getLocationOnScreen(mTempLoc);\n\n\t\t\t\tif (rawX > mTempLoc[0] && rawY > mTempLoc[1] &&\n\t\t\t\t\t\trawX < mTempLoc[0] + dragBox.getWidth() &&\n\t\t\t\t\t\trawY < mTempLoc[1] + dragBox.getHeight()) {\n\n\t\t\t\t\tmItemX = item.getLeft();\n\t\t\t\t\tmItemY = item.getTop();\n\n\t\t\t\t\treturn touchPos;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn MISS;\n\t}\n\n\t@Override\n\tpublic boolean onDown(MotionEvent ev) {\n\t\tif (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {\n\t\t\tmClickRemoveHitPos = viewIdHitPosition(ev, mClickRemoveId);\n\t\t}\n\n\t\tmHitPos = startDragPosition(ev);\n\t\tif (mHitPos != MISS && mDragInitMode == ON_DOWN) {\n\t\t\tstartDrag(mHitPos, (int) ev.getX() - mItemX, (int) ev.getY() - mItemY);\n\t\t}\n\n\t\tmIsRemoving = false;\n\t\tmCanDrag = true;\n\t\tmPositionX = 0;\n\t\tmFlingHitPos = startFlingPosition(ev);\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n\t\tif (e1 == null || e2 == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal int x1 = (int) e1.getX();\n\t\tfinal int y1 = (int) e1.getY();\n\t\tfinal int x2 = (int) e2.getX();\n\t\tfinal int y2 = (int) e2.getY();\n\t\tfinal int deltaX = x2 - mItemX;\n\t\tfinal int deltaY = y2 - mItemY;\n\n\t\tif (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) {\n\t\t\tif (mHitPos != MISS) {\n\t\t\t\tif (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) {\n\t\t\t\t\tstartDrag(mHitPos, deltaX, deltaY);\n\t\t\t\t} else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) {\n\t\t\t\t\tmIsRemoving = true;\n\t\t\t\t\tstartDrag(mFlingHitPos, deltaX, deltaY);\n\t\t\t\t}\n\t\t\t} else if (mFlingHitPos != MISS) {\n\t\t\t\tif (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) {\n\t\t\t\t\tmIsRemoving = true;\n\t\t\t\t\tstartDrag(mFlingHitPos, deltaX, deltaY);\n\t\t\t\t} else if (Math.abs(y2 - y1) > mTouchSlop) {\n\t\t\t\t\tmCanDrag = false; // if started to scroll the list then\n\t\t\t\t\t// don't allow sorting nor fling-removing\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void onLongPress(MotionEvent e) {\n\t\t// Log.d(\"mobeta\", \"lift listener long pressed\");\n\t\tif (mHitPos != MISS && mDragInitMode == ON_LONG_PRESS) {\n\t\t\tmDslv.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);\n\t\t\tstartDrag(mHitPos, mCurrX - mItemX, mCurrY - mItemY);\n\t\t}\n\t}\n\n\t// complete the OnGestureListener interface\n\t@Override\n\tpublic final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n\t\treturn false;\n\t}\n\n\t// complete the OnGestureListener interface\n\t@Override\n\tpublic boolean onSingleTapUp(MotionEvent ev) {\n\t\tif (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {\n\t\t\tif (mClickRemoveHitPos != MISS) {\n\t\t\t\tmDslv.removeItem(mClickRemoveHitPos - mDslv.getHeaderViewsCount());\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t// complete the OnGestureListener interface\n\t@Override\n\tpublic void onShowPress(MotionEvent ev) {\n\t\t// do nothing\n\t}\n\n\tpublic boolean isDragging() {\n\t\treturn mDragging;\n\t}\n\n\tprivate GestureDetector.OnGestureListener mFlingRemoveListener =\n\t\t\tnew GestureDetector.SimpleOnGestureListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,\n\t\t\t\t\t\t\t\t\t\t\t float velocityY) {\n\t\t\t\t\t// Log.d(\"mobeta\", \"on fling remove called\");\n\t\t\t\t\tif (mRemoveEnabled && mIsRemoving) {\n\t\t\t\t\t\tint w = mDslv.getWidth();\n\t\t\t\t\t\tint minPos = w / 5;\n\t\t\t\t\t\tif (velocityX > mFlingSpeed) {\n\t\t\t\t\t\t\tif (mPositionX > -minPos) {\n\t\t\t\t\t\t\t\tmDslv.stopDragWithVelocity(true, velocityX);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (velocityX < -mFlingSpeed) {\n\t\t\t\t\t\t\tif (mPositionX < minPos) {\n\t\t\t\t\t\t\t\tmDslv.stopDragWithVelocity(true, velocityX);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmIsRemoving = false;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t};\n\n}\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortCursorAdapter.java",
    "content": "package com.mobeta.android.dslv;\n\nimport java.util.ArrayList;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.util.SparseIntArray;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.cursoradapter.widget.CursorAdapter;\n\n\n/**\n * A subclass of {@link android.widget.CursorAdapter} that provides\n * reordering of the elements in the Cursor based on completed\n * drag-sort operations. The reordering is a simple mapping of\n * list positions into Cursor positions (the Cursor is unchanged).\n * To persist changes made by drag-sorts, one can retrieve the\n * mapping with the {@link #getCursorPositions()} method, which\n * returns the reordered list of Cursor positions.\n *\n * An instance of this class is passed\n * to {@link DragSortListView#setAdapter(ListAdapter)} and, since\n * this class implements the {@link DragSortListView.DragSortListener}\n * interface, it is automatically set as the DragSortListener for\n * the DragSortListView instance.\n */\npublic abstract class DragSortCursorAdapter extends CursorAdapter implements DragSortListView.DragSortListener {\n\n\tprivate static final int REMOVED = -1;\n\n\t/**\n\t * Key is ListView position, value is Cursor position\n\t */\n\tprivate SparseIntArray mListMapping = new SparseIntArray();\n\n\tprivate ArrayList<Integer> mRemovedCursorPositions = new ArrayList<Integer>();\n\n\t@Deprecated\n\tpublic DragSortCursorAdapter(Context context, Cursor c) {\n\t\tsuper(context, c);\n\t}\n\n\tpublic DragSortCursorAdapter(Context context, Cursor c, boolean autoRequery) {\n\t\tsuper(context, c, autoRequery);\n\t}\n\n\tpublic DragSortCursorAdapter(Context context, Cursor c, int flags) {\n\t\tsuper(context, c, flags);\n\t}\n\n\t/**\n\t * Swaps Cursor and clears list-Cursor mapping.\n\t *\n\t * @see android.widget.CursorAdapter#swapCursor(android.database.Cursor)\n\t */\n\t@Override\n\tpublic Cursor swapCursor(Cursor newCursor) {\n\t\tCursor old = super.swapCursor(newCursor);\n\t\tresetMappings();\n\t\treturn old;\n\t}\n\n\t/**\n\t * Changes Cursor and clears list-Cursor mapping.\n\t *\n\t * @see android.widget.CursorAdapter#changeCursor(android.database.Cursor)\n\t */\n\t@Override\n\tpublic void changeCursor(Cursor cursor) {\n\t\tsuper.changeCursor(cursor);\n\t\tresetMappings();\n\t}\n\n\t/**\n\t * Resets list-cursor mapping.\n\t */\n\tpublic void reset() {\n\t\tresetMappings();\n\t\tnotifyDataSetChanged();\n\t}\n\n\tprivate void resetMappings() {\n\t\tmListMapping.clear();\n\t\tmRemovedCursorPositions.clear();\n\t}\n\n\t@Override\n\tpublic Object getItem(int position) {\n\t\treturn super.getItem(mListMapping.get(position, position));\n\t}\n\n\t@Override\n\tpublic long getItemId(int position) {\n\t\treturn super.getItemId(mListMapping.get(position, position));\n\t}\n\n\t@Override\n\tpublic View getDropDownView(int position, View convertView, ViewGroup parent) {\n\t\treturn super.getDropDownView(mListMapping.get(position, position), convertView, parent);\n\t}\n\n\t@Override\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\treturn super.getView(mListMapping.get(position, position), convertView, parent);\n\t}\n\n\t/**\n\t * On drop, this updates the mapping between Cursor positions\n\t * and ListView positions. The Cursor is unchanged. Retrieve\n\t * the current mapping with {@link getCursorPositions()}.\n\t *\n\t * @see DragSortListView.DropListener#drop(int, int)\n\t */\n\t@Override\n\tpublic void drop(int from, int to) {\n\t\tif (from != to) {\n\t\t\tint cursorFrom = mListMapping.get(from, from);\n\n\t\t\tif (from > to) {\n\t\t\t\tfor (int i = from; i > to; --i) {\n\t\t\t\t\tmListMapping.put(i, mListMapping.get(i - 1, i - 1));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (int i = from; i < to; ++i) {\n\t\t\t\t\tmListMapping.put(i, mListMapping.get(i + 1, i + 1));\n\t\t\t\t}\n\t\t\t}\n\t\t\tmListMapping.put(to, cursorFrom);\n\n\t\t\tcleanMapping();\n\t\t\tnotifyDataSetChanged();\n\t\t}\n\t}\n\n\t/**\n\t * On remove, this updates the mapping between Cursor positions\n\t * and ListView positions. The Cursor is unchanged. Retrieve\n\t * the current mapping with {@link getCursorPositions()}.\n\t *\n\t * @see DragSortListView.RemoveListener#remove(int)\n\t */\n\t@Override\n\tpublic void remove(int which) {\n\t\tint cursorPos = mListMapping.get(which, which);\n\t\tif (!mRemovedCursorPositions.contains(cursorPos)) {\n\t\t\tmRemovedCursorPositions.add(cursorPos);\n\t\t}\n\n\t\tint newCount = getCount();\n\t\tfor (int i = which; i < newCount; ++i) {\n\t\t\tmListMapping.put(i, mListMapping.get(i + 1, i + 1));\n\t\t}\n\n\t\tmListMapping.delete(newCount);\n\n\t\tcleanMapping();\n\t\tnotifyDataSetChanged();\n\t}\n\n\t/**\n\t * Does nothing. Just completes DragSortListener interface.\n\t */\n\t@Override\n\tpublic void drag(int from, int to) {\n\t\t// do nothing\n\t}\n\n\t/**\n\t * Remove unnecessary mappings from sparse array.\n\t */\n\tprivate void cleanMapping() {\n\t\tArrayList<Integer> toRemove = new ArrayList<Integer>();\n\n\t\tfinal int size = mListMapping.size();\n\t\tfor (int i = 0; i < size; ++i) {\n\t\t\tif (mListMapping.keyAt(i) == mListMapping.valueAt(i)) {\n\t\t\t\ttoRemove.add(mListMapping.keyAt(i));\n\t\t\t}\n\t\t}\n\n\t\tfor (int position : toRemove) {\n\t\t\tmListMapping.delete(position);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getCount() {\n\t\treturn super.getCount() - mRemovedCursorPositions.size();\n\t}\n\n\t/**\n\t * Get the Cursor position mapped to by the provided list position\n\t * (given all previously handled drag-sort\n\t * operations).\n\t *\n\t * @param position List position\n\t * @return The mapped-to Cursor position\n\t */\n\tpublic int getCursorPosition(int position) {\n\t\treturn mListMapping.get(position, position);\n\t}\n\n\t/**\n\t * Get the current order of Cursor positions presented by the\n\t * list.\n\t */\n\tpublic ArrayList<Integer> getCursorPositions() {\n\t\tArrayList<Integer> result = new ArrayList<Integer>();\n\n\t\tfor (int i = 0; i < getCount(); ++i) {\n\t\t\tresult.add(mListMapping.get(i, i));\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get the list position mapped to by the provided Cursor position.\n\t * If the provided Cursor position has been removed by a drag-sort,\n\t * this returns {@link #REMOVED}.\n\t *\n\t * @param cursorPosition A Cursor position\n\t * @return The mapped-to list position or REMOVED\n\t */\n\tpublic int getListPosition(int cursorPosition) {\n\t\tif (mRemovedCursorPositions.contains(cursorPosition)) {\n\t\t\treturn REMOVED;\n\t\t}\n\n\t\tint index = mListMapping.indexOfValue(cursorPosition);\n\t\tif (index < 0) {\n\t\t\treturn cursorPosition;\n\t\t} else {\n\t\t\treturn mListMapping.keyAt(index);\n\t\t}\n\t}\n\n\n}\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortItemView.java",
    "content": "package com.mobeta.android.dslv;\n\nimport android.content.Context;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.View.MeasureSpec;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.util.Log;\n\n/**\n * Lightweight ViewGroup that wraps list items obtained from user's\n * ListAdapter. ItemView expects a single child that has a definite\n * height (i.e. the child's layout height is not MATCH_PARENT).\n * The width of\n * ItemView will always match the width of its child (that is,\n * the width MeasureSpec given to ItemView is passed directly\n * to the child, and the ItemView measured width is set to the\n * child's measured width). The height of ItemView can be anything;\n * the\n *\n *\n * The purpose of this class is to optimize slide\n * shuffle animations.\n */\npublic class DragSortItemView extends ViewGroup {\n\n\tprivate int mGravity = Gravity.TOP;\n\n\tpublic DragSortItemView(Context context) {\n\t\tsuper(context);\n\n\t\t// always init with standard ListView layout params\n\t\tsetLayoutParams(new AbsListView.LayoutParams(\n\t\t\t\tViewGroup.LayoutParams.MATCH_PARENT,\n\t\t\t\tViewGroup.LayoutParams.WRAP_CONTENT));\n\n\t\t//setClipChildren(true);\n\t}\n\n\tpublic void setGravity(int gravity) {\n\t\tmGravity = gravity;\n\t}\n\n\tpublic int getGravity() {\n\t\treturn mGravity;\n\t}\n\n\t@Override\n\tprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n\t\tfinal View child = getChildAt(0);\n\n\t\tif (child == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (mGravity == Gravity.TOP) {\n\t\t\tchild.layout(0, 0, getMeasuredWidth(), child.getMeasuredHeight());\n\t\t} else {\n\t\t\tchild.layout(0, getMeasuredHeight() - child.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight());\n\t\t}\n\t}\n\n\t/**\n\t *\n\t */\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\n\t\tint height = MeasureSpec.getSize(heightMeasureSpec);\n\t\tint width = MeasureSpec.getSize(widthMeasureSpec);\n\n\t\tint heightMode = MeasureSpec.getMode(heightMeasureSpec);\n\n\t\tfinal View child = getChildAt(0);\n\t\tif (child == null) {\n\t\t\tsetMeasuredDimension(0, width);\n\t\t\treturn;\n\t\t}\n\n\t\tif (child.isLayoutRequested()) {\n\t\t\t// Always let child be as tall as it wants.\n\t\t\tmeasureChild(child, widthMeasureSpec,\n\t\t\t\t\tMeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));\n\t\t}\n\n\t\tif (heightMode == MeasureSpec.UNSPECIFIED) {\n\t\t\tViewGroup.LayoutParams lp = getLayoutParams();\n\n\t\t\tif (lp.height > 0) {\n\t\t\t\theight = lp.height;\n\t\t\t} else {\n\t\t\t\theight = child.getMeasuredHeight();\n\t\t\t}\n\t\t}\n\n\t\tsetMeasuredDimension(width, height);\n\t}\n\n}\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortItemViewCheckable.java",
    "content": "package com.mobeta.android.dslv;\n\nimport android.content.Context;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.View.MeasureSpec;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.Checkable;\nimport android.util.Log;\n\n/**\n * Lightweight ViewGroup that wraps list items obtained from user's\n * ListAdapter. ItemView expects a single child that has a definite\n * height (i.e. the child's layout height is not MATCH_PARENT).\n * The width of\n * ItemView will always match the width of its child (that is,\n * the width MeasureSpec given to ItemView is passed directly\n * to the child, and the ItemView measured width is set to the\n * child's measured width). The height of ItemView can be anything;\n * the\n *\n *\n * The purpose of this class is to optimize slide\n * shuffle animations.\n */\npublic class DragSortItemViewCheckable extends DragSortItemView implements Checkable {\n\n\tpublic DragSortItemViewCheckable(Context context) {\n\t\tsuper(context);\n\t}\n\n\t@Override\n\tpublic boolean isChecked() {\n\t\tView child = getChildAt(0);\n\t\tif (child instanceof Checkable)\n\t\t\treturn ((Checkable) child).isChecked();\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setChecked(boolean checked) {\n\t\tView child = getChildAt(0);\n\t\tif (child instanceof Checkable)\n\t\t\t((Checkable) child).setChecked(checked);\n\t}\n\n\t@Override\n\tpublic void toggle() {\n\t\tView child = getChildAt(0);\n\t\tif (child instanceof Checkable)\n\t\t\t((Checkable) child).toggle();\n\t}\n}\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortListView.java",
    "content": "/*\n * DragSortListView.\n *\n * A subclass of the Android ListView component that enables drag\n * and drop re-ordering of list items.\n *\n * Copyright 2012 Carl Bauer\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.mobeta.android.dslv;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.database.DataSetObserver;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Point;\nimport android.graphics.drawable.Drawable;\nimport android.os.Environment;\nimport android.os.SystemClock;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.util.SparseBooleanArray;\nimport android.util.SparseIntArray;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.View.OnTouchListener;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.BaseAdapter;\nimport android.widget.Checkable;\nimport android.widget.ListAdapter;\nimport android.widget.ListView;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.util.ArrayList;\n\n/**\n * ListView subclass that mediates drag and drop resorting of items.\n *\n * @author heycosmo\n */\npublic class DragSortListView extends ListView implements OnTouchListener {\n\n\n\t/**\n\t * The View that floats above the ListView and represents\n\t * the dragged item.\n\t */\n\tprivate View mFloatView;\n\n\t/**\n\t * The float View location. First based on touch location\n\t * and given deltaX and deltaY. Then restricted by callback\n\t * to FloatViewManager.onDragFloatView(). Finally restricted\n\t * by bounds of DSLV.\n\t */\n\tprivate Point mFloatLoc = new Point();\n\n\tprivate Point mTouchLoc = new Point();\n\n\t/**\n\t * The middle (in the y-direction) of the floating View.\n\t */\n\tprivate int mFloatViewMid;\n\n\t/**\n\t * Flag to make sure float View isn't measured twice\n\t */\n\tprivate boolean mFloatViewOnMeasured = false;\n\n\t/**\n\t * Watch the Adapter for data changes. Cancel a drag if\n\t * coincident with a change.\n\t */\n\tprivate DataSetObserver mObserver;\n\n\t/**\n\t * Transparency for the floating View (XML attribute).\n\t */\n\tprivate float mFloatAlpha = 1.0f;\n\tprivate float mCurrFloatAlpha = 1.0f;\n\n\t/**\n\t * While drag-sorting, the current position of the floating\n\t * View. If dropped, the dragged item will land in this position.\n\t */\n\tprivate int mFloatPos;\n\n\t/**\n\t * The first expanded ListView position that helps represent\n\t * the drop slot tracking the floating View.\n\t */\n\tprivate int mFirstExpPos;\n\n\t/**\n\t * The second expanded ListView position that helps represent\n\t * the drop slot tracking the floating View. This can equal\n\t * mFirstExpPos if there is no slide shuffle occurring; otherwise\n\t * it is equal to mFirstExpPos + 1.\n\t */\n\tprivate int mSecondExpPos;\n\n\t/**\n\t * Flag set if slide shuffling is enabled.\n\t */\n\tprivate boolean mAnimate = false;\n\n\t/**\n\t * The user dragged from this position.\n\t */\n\tprivate int mSrcPos;\n\n\t/**\n\t * Offset (in x) within the dragged item at which the user\n\t * picked it up (or first touched down with the digitalis).\n\t */\n\tprivate int mDragDeltaX;\n\n\t/**\n\t * Offset (in y) within the dragged item at which the user\n\t * picked it up (or first touched down with the digitalis).\n\t */\n\tprivate int mDragDeltaY;\n\n\n\t/**\n\t * The difference (in x) between screen coordinates and coordinates\n\t * in this view.\n\t */\n\tprivate int mOffsetX;\n\n\t/**\n\t * The difference (in y) between screen coordinates and coordinates\n\t * in this view.\n\t */\n\tprivate int mOffsetY;\n\n\t/**\n\t * A listener that receives callbacks whenever the floating View\n\t * hovers over a new position.\n\t */\n\tprivate DragListener mDragListener;\n\n\t/**\n\t * A listener that receives a callback when the floating View\n\t * is dropped.\n\t */\n\tprivate DropListener mDropListener;\n\n\t/**\n\t * A listener that receives a callback when the floating View\n\t * (or more precisely the originally dragged item) is removed\n\t * by one of the provided gestures.\n\t */\n\tprivate RemoveListener mRemoveListener;\n\n\t/**\n\t * Enable/Disable item dragging\n\t *\n\t * @attr name dslv:drag_enabled\n\t */\n\tprivate boolean mDragEnabled = true;\n\n\t/**\n\t * Drag state enum.\n\t */\n\tprivate final static int IDLE = 0;\n\tprivate final static int REMOVING = 1;\n\tprivate final static int DROPPING = 2;\n\tprivate final static int STOPPED = 3;\n\tprivate final static int DRAGGING = 4;\n\n\tprivate int mDragState = IDLE;\n\n\t/**\n\t * Height in pixels to which the originally dragged item\n\t * is collapsed during a drag-sort. Currently, this value\n\t * must be greater than zero.\n\t */\n\tprivate int mItemHeightCollapsed = 1;\n\n\t/**\n\t * Height of the floating View. Stored for the purpose of\n\t * providing the tracking drop slot.\n\t */\n\tprivate int mFloatViewHeight;\n\n\t/**\n\t * Convenience member. See above.\n\t */\n\tprivate int mFloatViewHeightHalf;\n\n\t/**\n\t * Save the given width spec for use in measuring children\n\t */\n\tprivate int mWidthMeasureSpec = 0;\n\n\t/**\n\t * Sample Views ultimately used for calculating the height\n\t * of ListView items that are off-screen.\n\t */\n\tprivate View[] mSampleViewTypes = new View[1];\n\n\t/**\n\t * Drag-scroll encapsulator!\n\t */\n\tprivate DragScroller mDragScroller;\n\n\t/**\n\t * Determines the start of the upward drag-scroll region\n\t * at the top of the ListView. Specified by a fraction\n\t * of the ListView height, thus screen resolution agnostic.\n\t */\n\tprivate float mDragUpScrollStartFrac = 1.0f / 3.0f;\n\n\t/**\n\t * Determines the start of the downward drag-scroll region\n\t * at the bottom of the ListView. Specified by a fraction\n\t * of the ListView height, thus screen resolution agnostic.\n\t */\n\tprivate float mDragDownScrollStartFrac = 1.0f / 3.0f;\n\n\t/**\n\t * The following are calculated from the above fracs.\n\t */\n\tprivate int mUpScrollStartY;\n\tprivate int mDownScrollStartY;\n\tprivate float mDownScrollStartYF;\n\tprivate float mUpScrollStartYF;\n\n\t/**\n\t * Calculated from above above and current ListView height.\n\t */\n\tprivate float mDragUpScrollHeight;\n\n\t/**\n\t * Calculated from above above and current ListView height.\n\t */\n\tprivate float mDragDownScrollHeight;\n\n\t/**\n\t * Maximum drag-scroll speed in pixels per ms. Only used with\n\t * default linear drag-scroll profile.\n\t */\n\tprivate float mMaxScrollSpeed = 0.5f;\n\n\t/**\n\t * Defines the scroll speed during a drag-scroll. User can\n\t * provide their own; this default is a simple linear profile\n\t * where scroll speed increases linearly as the floating View\n\t * nears the top/bottom of the ListView.\n\t */\n\tprivate DragScrollProfile mScrollProfile = new DragScrollProfile() {\n\t\t@Override\n\t\tpublic float getSpeed(float w, long t) {\n\t\t\treturn mMaxScrollSpeed * w;\n\t\t}\n\t};\n\n\t/**\n\t * Current touch x.\n\t */\n\tprivate int mX;\n\n\t/**\n\t * Current touch y.\n\t */\n\tprivate int mY;\n\n\t/**\n\t * Last touch x.\n\t */\n\tprivate int mLastX;\n\n\t/**\n\t * Last touch y.\n\t */\n\tprivate int mLastY;\n\n\t/**\n\t * The touch y-coord at which drag started\n\t */\n\tprivate int mDragStartY;\n\n\t/**\n\t * Drag flag bit. Floating View can move in the positive\n\t * x direction.\n\t */\n\tpublic final static int DRAG_POS_X = 0x1;\n\n\t/**\n\t * Drag flag bit. Floating View can move in the negative\n\t * x direction.\n\t */\n\tpublic final static int DRAG_NEG_X = 0x2;\n\n\t/**\n\t * Drag flag bit. Floating View can move in the positive\n\t * y direction. This is subtle. What this actually means is\n\t * that, if enabled, the floating View can be dragged below its starting\n\t * position. Remove in favor of upper-bounding item position?\n\t */\n\tpublic final static int DRAG_POS_Y = 0x4;\n\n\t/**\n\t * Drag flag bit. Floating View can move in the negative\n\t * y direction. This is subtle. What this actually means is\n\t * that the floating View can be dragged above its starting\n\t * position. Remove in favor of lower-bounding item position?\n\t */\n\tpublic final static int DRAG_NEG_Y = 0x8;\n\n\t/**\n\t * Flags that determine limits on the motion of the\n\t * floating View. See flags above.\n\t */\n\tprivate int mDragFlags = 0;\n\n\t/**\n\t * Last call to an on*TouchEvent was a call to\n\t * onInterceptTouchEvent.\n\t */\n\tprivate boolean mLastCallWasIntercept = false;\n\n\t/**\n\t * A touch event is in progress.\n\t */\n\tprivate boolean mInTouchEvent = false;\n\n\t/**\n\t * Let the user customize the floating View.\n\t */\n\tprivate FloatViewManager mFloatViewManager = null;\n\n\t/**\n\t * Given to ListView to cancel its action when a drag-sort\n\t * begins.\n\t */\n\tprivate MotionEvent mCancelEvent;\n\n\t/**\n\t * Enum telling where to cancel the ListView action when a\n\t * drag-sort begins\n\t */\n\tprivate static final int NO_CANCEL = 0;\n\tprivate static final int ON_TOUCH_EVENT = 1;\n\tprivate static final int ON_INTERCEPT_TOUCH_EVENT = 2;\n\n\t/**\n\t * Where to cancel the ListView action when a\n\t * drag-sort begins\n\t */\n\tprivate int mCancelMethod = NO_CANCEL;\n\n\t/**\n\t * Determines when a slide shuffle animation starts. That is,\n\t * defines how close to the edge of the drop slot the floating\n\t * View must be to initiate the slide.\n\t */\n\tprivate float mSlideRegionFrac = 0.25f;\n\n\t/**\n\t * Number between 0 and 1 indicating the relative location of\n\t * a sliding item (only used if drag-sort animations\n\t * are turned on). Nearly 1 means the item is\n\t * at the top of the slide region (nearly full blank item\n\t * is directly below).\n\t */\n\tprivate float mSlideFrac = 0.0f;\n\n\t/**\n\t * Wraps the user-provided ListAdapter. This is used to wrap each\n\t * item View given by the user inside another View (currenly\n\t * a RelativeLayout) which\n\t * expands and collapses to simulate the item shuffling.\n\t */\n\tprivate AdapterWrapper mAdapterWrapper;\n\n\t/**\n\t * Turn on custom debugger.\n\t */\n\tprivate boolean mTrackDragSort = false;\n\n\t/**\n\t * Debugging class.\n\t */\n\tprivate DragSortTracker mDragSortTracker;\n\n\t/**\n\t * Needed for adjusting item heights from within layoutChildren\n\t */\n\tprivate boolean mBlockLayoutRequests = false;\n\n\t/**\n\t * Set to true when a down event happens during drag sort;\n\t * for example, when drag finish animations are\n\t * playing.\n\t */\n\tprivate boolean mIgnoreTouchEvent = false;\n\n\t/**\n\t * Caches DragSortItemView child heights. Sometimes DSLV has to\n\t * know the height of an offscreen item. Since ListView virtualizes\n\t * these, DSLV must get the item from the ListAdapter to obtain\n\t * its height. That process can be expensive, but often the same\n\t * offscreen item will be requested many times in a row. Once an\n\t * offscreen item height is calculated, we cache it in this guy.\n\t * Actually, we cache the height of the child of the\n\t * DragSortItemView since the item height changes often during a\n\t * drag-sort.\n\t */\n\tprivate static final int sCacheSize = 3;\n\tprivate HeightCache mChildHeightCache = new HeightCache(sCacheSize);\n\n\tprivate RemoveAnimator mRemoveAnimator;\n\n\tprivate LiftAnimator mLiftAnimator;\n\n\tprivate DropAnimator mDropAnimator;\n\n\tprivate boolean mUseRemoveVelocity;\n\tprivate float mRemoveVelocityX = 0;\n\n\tprivate DragSortController controller = null;\n\n\tprivate OnTouchListener onTouchListener;\n\n\tpublic DragSortListView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\n\t\tint defaultDuration = 150;\n\t\tint removeAnimDuration = defaultDuration; // ms\n\t\tint dropAnimDuration = defaultDuration; // ms\n\n\t\tif (attrs != null) {\n\t\t\tTypedArray a = getContext().obtainStyledAttributes(attrs,\n\t\t\t\t\tR.styleable.DragSortListView, 0, 0);\n\n\t\t\tmItemHeightCollapsed = Math.max(1, a.getDimensionPixelSize(\n\t\t\t\t\tR.styleable.DragSortListView_collapsed_height, 1));\n\n\t\t\tmTrackDragSort = a.getBoolean(\n\t\t\t\t\tR.styleable.DragSortListView_track_drag_sort, false);\n\n\t\t\tif (mTrackDragSort) {\n\t\t\t\tmDragSortTracker = new DragSortTracker();\n\t\t\t}\n\n\t\t\t// alpha between 0 and 255, 0=transparent, 255=opaque\n\t\t\tmFloatAlpha = a.getFloat(R.styleable.DragSortListView_float_alpha, mFloatAlpha);\n\t\t\tmCurrFloatAlpha = mFloatAlpha;\n\n\t\t\tmDragEnabled = a.getBoolean(R.styleable.DragSortListView_drag_enabled, mDragEnabled);\n\n\t\t\tmSlideRegionFrac = Math.max(0.0f,\n\t\t\t\t\tMath.min(1.0f, 1.0f - a.getFloat(\n\t\t\t\t\t\t\tR.styleable.DragSortListView_slide_shuffle_speed,\n\t\t\t\t\t\t\t0.75f)));\n\n\t\t\tmAnimate = mSlideRegionFrac > 0.0f;\n\n\t\t\tfloat frac = a.getFloat(\n\t\t\t\t\tR.styleable.DragSortListView_drag_scroll_start,\n\t\t\t\t\tmDragUpScrollStartFrac);\n\n\t\t\tsetDragScrollStart(frac);\n\n\t\t\tmMaxScrollSpeed = a.getFloat(\n\t\t\t\t\tR.styleable.DragSortListView_max_drag_scroll_speed,\n\t\t\t\t\tmMaxScrollSpeed);\n\n\t\t\tremoveAnimDuration = a.getInt(\n\t\t\t\t\tR.styleable.DragSortListView_remove_animation_duration,\n\t\t\t\t\tremoveAnimDuration);\n\n\t\t\tdropAnimDuration = a.getInt(\n\t\t\t\t\tR.styleable.DragSortListView_drop_animation_duration,\n\t\t\t\t\tdropAnimDuration);\n\n\t\t\tboolean useDefault = a.getBoolean(\n\t\t\t\t\tR.styleable.DragSortListView_use_default_controller,\n\t\t\t\t\ttrue);\n\n\t\t\tif (useDefault) {\n\t\t\t\tboolean removeEnabled = a.getBoolean(\n\t\t\t\t\t\tR.styleable.DragSortListView_remove_enabled,\n\t\t\t\t\t\tfalse);\n\t\t\t\tint removeMode = a.getInt(\n\t\t\t\t\t\tR.styleable.DragSortListView_remove_mode,\n\t\t\t\t\t\tDragSortController.FLING_REMOVE);\n\t\t\t\tboolean sortEnabled = a.getBoolean(\n\t\t\t\t\t\tR.styleable.DragSortListView_sort_enabled,\n\t\t\t\t\t\ttrue);\n\t\t\t\tint dragInitMode = a.getInt(\n\t\t\t\t\t\tR.styleable.DragSortListView_drag_start_mode,\n\t\t\t\t\t\tDragSortController.ON_DOWN);\n\t\t\t\tint dragHandleId = a.getResourceId(\n\t\t\t\t\t\tR.styleable.DragSortListView_drag_handle_id,\n\t\t\t\t\t\t0);\n\t\t\t\tint flingHandleId = a.getResourceId(\n\t\t\t\t\t\tR.styleable.DragSortListView_fling_handle_id,\n\t\t\t\t\t\t0);\n\t\t\t\tint clickRemoveId = a.getResourceId(\n\t\t\t\t\t\tR.styleable.DragSortListView_click_remove_id,\n\t\t\t\t\t\t0);\n\t\t\t\tint bgColor = a.getColor(\n\t\t\t\t\t\tR.styleable.DragSortListView_float_background_color,\n\t\t\t\t\t\tColor.BLACK);\n\n\t\t\t\tcontroller = new DragSortController(\n\t\t\t\t\t\tthis, dragHandleId, dragInitMode, removeMode,\n\t\t\t\t\t\tclickRemoveId, flingHandleId);\n\t\t\t\tcontroller.setRemoveEnabled(removeEnabled);\n\t\t\t\tcontroller.setSortEnabled(sortEnabled);\n\t\t\t\tcontroller.setBackgroundColor(bgColor);\n\n\t\t\t\tmFloatViewManager = controller;\n\t\t\t\tsuper.setOnTouchListener(this);\n\t\t\t}\n\n\t\t\ta.recycle();\n\t\t}\n\n\t\tmDragScroller = new DragScroller();\n\n\t\tfloat smoothness = 0.5f;\n\t\tif (removeAnimDuration > 0) {\n\t\t\tmRemoveAnimator = new RemoveAnimator(smoothness, removeAnimDuration);\n\t\t}\n\t\t// mLiftAnimator = new LiftAnimator(smoothness, 100);\n\t\tif (dropAnimDuration > 0) {\n\t\t\tmDropAnimator = new DropAnimator(smoothness, dropAnimDuration);\n\t\t}\n\n\t\tmCancelEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0f, 0f, 0, 0f,\n\t\t\t\t0f, 0, 0);\n\n\t\t// construct the dataset observer\n\t\tmObserver = new DataSetObserver() {\n\t\t\tprivate void cancel() {\n\t\t\t\tif (mDragState == DRAGGING) {\n\t\t\t\t\tcancelDrag();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onChanged() {\n\t\t\t\tcancel();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onInvalidated() {\n\t\t\t\tcancel();\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setOnTouchListener(OnTouchListener tl) {\n\t\tonTouchListener = tl;\n\t}\n\n\t@Override\n\tpublic boolean onTouch(View v, MotionEvent ev) {\n\t\t// Only call listener if we are not dragging\n\t\tboolean result = controller.onTouch(v, ev);\n\t\tif (!controller.isDragging() && onTouchListener != null) {\n\t\t\tresult |= onTouchListener.onTouch(v, ev);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Usually called from a FloatViewManager. The float alpha\n\t * will be reset to the xml-defined value every time a drag\n\t * is stopped.\n\t */\n\tpublic void setFloatAlpha(float alpha) {\n\t\tmCurrFloatAlpha = alpha;\n\t}\n\n\tpublic float getFloatAlpha() {\n\t\treturn mCurrFloatAlpha;\n\t}\n\n\t/**\n\t * Set maximum drag scroll speed in positions/second. Only applies\n\t * if using default ScrollSpeedProfile.\n\t *\n\t * @param max Maximum scroll speed.\n\t */\n\tpublic void setMaxScrollSpeed(float max) {\n\t\tmMaxScrollSpeed = max;\n\t}\n\n\t/**\n\t * For each DragSortListView Listener interface implemented by\n\t * <code>adapter</code>, this method calls the appropriate\n\t * set*Listener method with <code>adapter</code> as the argument.\n\t *\n\t * @param adapter The ListAdapter providing data to back\n\t *                DragSortListView.\n\t * @see android.widget.ListView#setAdapter(android.widget.ListAdapter)\n\t */\n\t@Override\n\tpublic void setAdapter(ListAdapter adapter) {\n\t\tif (adapter != null) {\n\t\t\tmAdapterWrapper = new AdapterWrapper(adapter);\n\t\t\tadapter.registerDataSetObserver(mObserver);\n\n\t\t\tif (adapter instanceof DropListener) {\n\t\t\t\tsetDropListener((DropListener) adapter);\n\t\t\t}\n\t\t\tif (adapter instanceof DragListener) {\n\t\t\t\tsetDragListener((DragListener) adapter);\n\t\t\t}\n\t\t\tif (adapter instanceof RemoveListener) {\n\t\t\t\tsetRemoveListener((RemoveListener) adapter);\n\t\t\t}\n\t\t} else {\n\t\t\tmAdapterWrapper = null;\n\t\t}\n\n\t\tsuper.setAdapter(mAdapterWrapper);\n\t}\n\n\t/**\n\t * As opposed to {@link ListView#getAdapter()}, which returns\n\t * a heavily wrapped ListAdapter (DragSortListView wraps the\n\t * input ListAdapter {\\emph and} ListView wraps the wrapped one).\n\t *\n\t * @return The ListAdapter set as the argument of {@link setAdapter()}\n\t */\n\tpublic ListAdapter getInputAdapter() {\n\t\tif (mAdapterWrapper == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn mAdapterWrapper.getAdapter();\n\t\t}\n\t}\n\n\tprivate class AdapterWrapper extends BaseAdapter {\n\t\tprivate ListAdapter mAdapter;\n\n\t\tpublic AdapterWrapper(ListAdapter adapter) {\n\t\t\tsuper();\n\t\t\tmAdapter = adapter;\n\n\t\t\tmAdapter.registerDataSetObserver(new DataSetObserver() {\n\t\t\t\tpublic void onChanged() {\n\t\t\t\t\tnotifyDataSetChanged();\n\t\t\t\t}\n\n\t\t\t\tpublic void onInvalidated() {\n\t\t\t\t\tnotifyDataSetInvalidated();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tpublic ListAdapter getAdapter() {\n\t\t\treturn mAdapter;\n\t\t}\n\n\t\t@Override\n\t\tpublic long getItemId(int position) {\n\t\t\treturn mAdapter.getItemId(position);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getItem(int position) {\n\t\t\treturn mAdapter.getItem(position);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getCount() {\n\t\t\treturn mAdapter.getCount();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean areAllItemsEnabled() {\n\t\t\treturn mAdapter.areAllItemsEnabled();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isEnabled(int position) {\n\t\t\treturn mAdapter.isEnabled(position);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getItemViewType(int position) {\n\t\t\treturn mAdapter.getItemViewType(position);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getViewTypeCount() {\n\t\t\treturn mAdapter.getViewTypeCount();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasStableIds() {\n\t\t\treturn mAdapter.hasStableIds();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isEmpty() {\n\t\t\treturn mAdapter.isEmpty();\n\t\t}\n\n\n\t\t@Override\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\n\t\t\tDragSortItemView v;\n\t\t\tView child;\n\t\t\t// Log.d(\"mobeta\", \"getView: position=\"+position+\" convertView=\"+convertView);\n\t\t\tif (convertView != null) {\n\t\t\t\tv = (DragSortItemView) convertView;\n\t\t\t\tView oldChild = v.getChildAt(0);\n\n\t\t\t\tchild = mAdapter.getView(position, oldChild, DragSortListView.this);\n\t\t\t\tif (child != oldChild) {\n\t\t\t\t\t// shouldn't get here if user is reusing convertViews\n\t\t\t\t\t// properly\n\t\t\t\t\tif (oldChild != null) {\n\t\t\t\t\t\tv.removeViewAt(0);\n\t\t\t\t\t}\n\t\t\t\t\tv.addView(child);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tchild = mAdapter.getView(position, null, DragSortListView.this);\n\t\t\t\tif (child instanceof Checkable) {\n\t\t\t\t\tv = new DragSortItemViewCheckable(getContext());\n\t\t\t\t} else {\n\t\t\t\t\tv = new DragSortItemView(getContext());\n\t\t\t\t}\n\t\t\t\tv.setLayoutParams(new AbsListView.LayoutParams(\n\t\t\t\t\t\tViewGroup.LayoutParams.MATCH_PARENT,\n\t\t\t\t\t\tViewGroup.LayoutParams.WRAP_CONTENT));\n\t\t\t\tv.addView(child);\n\t\t\t}\n\n\t\t\t// Set the correct item height given drag state; passed\n\t\t\t// View needs to be measured if measurement is required.\n\t\t\tadjustItem(position + getHeaderViewsCount(), v, true);\n\n\t\t\treturn v;\n\t\t}\n\t}\n\n\tprivate void drawDivider(int expPosition, Canvas canvas) {\n\n\t\tfinal Drawable divider = getDivider();\n\t\tfinal int dividerHeight = getDividerHeight();\n\t\t// Log.d(\"mobeta\", \"div=\"+divider+\" divH=\"+dividerHeight);\n\n\t\tif (divider != null && dividerHeight != 0) {\n\t\t\tfinal ViewGroup expItem = (ViewGroup) getChildAt(expPosition\n\t\t\t\t\t- getFirstVisiblePosition());\n\t\t\tif (expItem != null) {\n\t\t\t\tfinal int left = getPaddingLeft();\n\t\t\t\tfinal int right = getWidth() - getPaddingRight();\n\t\t\t\tfinal int top;\n\t\t\t\tfinal int bottom;\n\n\t\t\t\tfinal int childHeight = expItem.getChildAt(0).getHeight();\n\n\t\t\t\tif (expPosition > mSrcPos) {\n\t\t\t\t\ttop = expItem.getTop() + childHeight;\n\t\t\t\t\tbottom = top + dividerHeight;\n\t\t\t\t} else {\n\t\t\t\t\tbottom = expItem.getBottom() - childHeight;\n\t\t\t\t\ttop = bottom - dividerHeight;\n\t\t\t\t}\n\t\t\t\t// Log.d(\"mobeta\", \"l=\"+l+\" t=\"+t+\" r=\"+r+\" b=\"+b);\n\n\t\t\t\t// Have to clip to support ColorDrawable on <= Gingerbread\n\t\t\t\tcanvas.save();\n\t\t\t\tcanvas.clipRect(left, top, right, bottom);\n\t\t\t\tdivider.setBounds(left, top, right, bottom);\n\t\t\t\tdivider.draw(canvas);\n\t\t\t\tcanvas.restore();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tprotected void dispatchDraw(Canvas canvas) {\n\t\tsuper.dispatchDraw(canvas);\n\n\t\tif (mDragState != IDLE) {\n\t\t\t// draw the divider over the expanded item\n\t\t\tif (mFirstExpPos != mSrcPos) {\n\t\t\t\tdrawDivider(mFirstExpPos, canvas);\n\t\t\t}\n\t\t\tif (mSecondExpPos != mFirstExpPos && mSecondExpPos != mSrcPos) {\n\t\t\t\tdrawDivider(mSecondExpPos, canvas);\n\t\t\t}\n\t\t}\n\n\t\tif (mFloatView != null) {\n\t\t\t// draw the float view over everything\n\t\t\tfinal int w = mFloatView.getWidth();\n\t\t\tfinal int h = mFloatView.getHeight();\n\n\t\t\tint x = mFloatLoc.x;\n\n\t\t\tfinal int lvWidth = getWidth();\n\t\t\tif (x < 0)\n\t\t\t\tx = -x;\n\t\t\tfloat alphaMod;\n\t\t\tif (x < lvWidth) {\n\t\t\t\talphaMod = ((float) (lvWidth - x)) / ((float) lvWidth);\n\t\t\t\talphaMod *= alphaMod;\n\t\t\t} else {\n\t\t\t\talphaMod = 0;\n\t\t\t}\n\n\t\t\tfinal int alpha = (int) (255f * mCurrFloatAlpha * alphaMod);\n\n\t\t\tcanvas.save();\n\t\t\t// Log.d(\"mobeta\", \"clip rect bounds: \" + canvas.getClipBounds());\n\t\t\tcanvas.translate(mFloatLoc.x, mFloatLoc.y);\n\t\t\tcanvas.clipRect(0, 0, w, h);\n\n\t\t\t// Log.d(\"mobeta\", \"clip rect bounds: \" + canvas.getClipBounds());\n\t\t\tcanvas.saveLayerAlpha(0, 0, w, h, alpha, Canvas.ALL_SAVE_FLAG);\n\t\t\tmFloatView.draw(canvas);\n\t\t\tcanvas.restore();\n\t\t\tcanvas.restore();\n\t\t}\n\t}\n\n\tprivate int getItemHeight(int position) {\n\t\tView v = getChildAt(position - getFirstVisiblePosition());\n\n\t\tif (v != null) {\n\t\t\t// item is onscreen, just get the height of the View\n\t\t\treturn v.getHeight();\n\t\t} else {\n\t\t\t// item is offscreen. get child height and calculate\n\t\t\t// item height based on current shuffle state\n\t\t\treturn calcItemHeight(position, getChildHeight(position));\n\t\t}\n\t}\n\n\tprivate void printPosData() {\n\t\tLog.d(\"mobeta\", \"mSrcPos=\" + mSrcPos + \" mFirstExpPos=\" + mFirstExpPos + \" mSecondExpPos=\"\n\t\t\t\t+ mSecondExpPos);\n\t}\n\n\tprivate class HeightCache {\n\n\t\tprivate SparseIntArray mMap;\n\t\tprivate ArrayList<Integer> mOrder;\n\t\tprivate int mMaxSize;\n\n\t\tpublic HeightCache(int size) {\n\t\t\tmMap = new SparseIntArray(size);\n\t\t\tmOrder = new ArrayList<Integer>(size);\n\t\t\tmMaxSize = size;\n\t\t}\n\n\t\t/**\n\t\t * Add item height at position if doesn't already exist.\n\t\t */\n\t\tpublic void add(int position, int height) {\n\t\t\tint currHeight = mMap.get(position, -1);\n\t\t\tif (currHeight != height) {\n\t\t\t\tif (currHeight == -1) {\n\t\t\t\t\tif (mMap.size() == mMaxSize) {\n\t\t\t\t\t\t// remove oldest entry\n\t\t\t\t\t\tmMap.delete(mOrder.remove(0));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// move position to newest slot\n\t\t\t\t\tmOrder.remove((Integer) position);\n\t\t\t\t}\n\t\t\t\tmMap.put(position, height);\n\t\t\t\tmOrder.add(position);\n\t\t\t}\n\t\t}\n\n\t\tpublic int get(int position) {\n\t\t\treturn mMap.get(position, -1);\n\t\t}\n\n\t\tpublic void clear() {\n\t\t\tmMap.clear();\n\t\t\tmOrder.clear();\n\t\t}\n\n\t}\n\n\t/**\n\t * Get the shuffle edge for item at position when top of\n\t * item is at y-coord top. Assumes that current item heights\n\t * are consistent with current float view location and\n\t * thus expanded positions and slide fraction. i.e. Should not be\n\t * called between update of expanded positions/slide fraction\n\t * and layoutChildren.\n\t *\n\t * @param position\n\t * @param top\n\t * @param height   Height of item at position. If -1, this function\n\t *                 calculates this height.\n\t * @return Shuffle line between position-1 and position (for\n\t * the given view of the list; that is, for when top of item at\n\t * position has y-coord of given `top`). If\n\t * floating View (treated as horizontal line) is dropped\n\t * immediately above this line, it lands in position-1. If\n\t * dropped immediately below this line, it lands in position.\n\t */\n\tprivate int getShuffleEdge(int position, int top) {\n\n\t\tfinal int numHeaders = getHeaderViewsCount();\n\t\tfinal int numFooters = getFooterViewsCount();\n\n\t\t// shuffle edges are defined between items that can be\n\t\t// dragged; there are N-1 of them if there are N draggable\n\t\t// items.\n\n\t\tif (position <= numHeaders || (position >= getCount() - numFooters)) {\n\t\t\treturn top;\n\t\t}\n\n\t\tint divHeight = getDividerHeight();\n\n\t\tint edge;\n\n\t\tint maxBlankHeight = mFloatViewHeight - mItemHeightCollapsed;\n\t\tint childHeight = getChildHeight(position);\n\t\tint itemHeight = getItemHeight(position);\n\n\t\t// first calculate top of item given that floating View is\n\t\t// centered over src position\n\t\tint otop = top;\n\t\tif (mSecondExpPos <= mSrcPos) {\n\t\t\t// items are expanded on and/or above the source position\n\n\t\t\tif (position == mSecondExpPos && mFirstExpPos != mSecondExpPos) {\n\t\t\t\tif (position == mSrcPos) {\n\t\t\t\t\totop = top + itemHeight - mFloatViewHeight;\n\t\t\t\t} else {\n\t\t\t\t\tint blankHeight = itemHeight - childHeight;\n\t\t\t\t\totop = top + blankHeight - maxBlankHeight;\n\t\t\t\t}\n\t\t\t} else if (position > mSecondExpPos && position <= mSrcPos) {\n\t\t\t\totop = top - maxBlankHeight;\n\t\t\t}\n\n\t\t} else {\n\t\t\t// items are expanded on and/or below the source position\n\n\t\t\tif (position > mSrcPos && position <= mFirstExpPos) {\n\t\t\t\totop = top + maxBlankHeight;\n\t\t\t} else if (position == mSecondExpPos && mFirstExpPos != mSecondExpPos) {\n\t\t\t\tint blankHeight = itemHeight - childHeight;\n\t\t\t\totop = top + blankHeight;\n\t\t\t}\n\t\t}\n\n\t\t// otop is set\n\t\tif (position <= mSrcPos) {\n\t\t\tedge = otop + (mFloatViewHeight - divHeight - getChildHeight(position - 1)) / 2;\n\t\t} else {\n\t\t\tedge = otop + (childHeight - divHeight - mFloatViewHeight) / 2;\n\t\t}\n\n\t\treturn edge;\n\t}\n\n\tprivate boolean updatePositions() {\n\n\t\tfinal int first = getFirstVisiblePosition();\n\t\tint startPos = mFirstExpPos;\n\t\tView startView = getChildAt(startPos - first);\n\n\t\tif (startView == null) {\n\t\t\tstartPos = first + getChildCount() / 2;\n\t\t\tstartView = getChildAt(startPos - first);\n\t\t}\n\t\tint startTop = startView.getTop();\n\n\t\tint itemHeight = startView.getHeight();\n\n\t\tint edge = getShuffleEdge(startPos, startTop);\n\t\tint lastEdge = edge;\n\n\t\tint divHeight = getDividerHeight();\n\n\t\t// Log.d(\"mobeta\", \"float mid=\"+mFloatViewMid);\n\n\t\tint itemPos = startPos;\n\t\tint itemTop = startTop;\n\t\tif (mFloatViewMid < edge) {\n\t\t\t// scanning up for float position\n\t\t\t// Log.d(\"mobeta\", \"    edge=\"+edge);\n\t\t\twhile (itemPos >= 0) {\n\t\t\t\titemPos--;\n\t\t\t\titemHeight = getItemHeight(itemPos);\n\n\t\t\t\tif (itemPos == 0) {\n\t\t\t\t\tedge = itemTop - divHeight - itemHeight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\titemTop -= itemHeight + divHeight;\n\t\t\t\tedge = getShuffleEdge(itemPos, itemTop);\n\t\t\t\t// Log.d(\"mobeta\", \"    edge=\"+edge);\n\n\t\t\t\tif (mFloatViewMid >= edge) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tlastEdge = edge;\n\t\t\t}\n\t\t} else {\n\t\t\t// scanning down for float position\n\t\t\t// Log.d(\"mobeta\", \"    edge=\"+edge);\n\t\t\tfinal int count = getCount();\n\t\t\twhile (itemPos < count) {\n\t\t\t\tif (itemPos == count - 1) {\n\t\t\t\t\tedge = itemTop + divHeight + itemHeight;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\titemTop += divHeight + itemHeight;\n\t\t\t\titemHeight = getItemHeight(itemPos + 1);\n\t\t\t\tedge = getShuffleEdge(itemPos + 1, itemTop);\n\t\t\t\t// Log.d(\"mobeta\", \"    edge=\"+edge);\n\n\t\t\t\t// test for hit\n\t\t\t\tif (mFloatViewMid < edge) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tlastEdge = edge;\n\t\t\t\titemPos++;\n\t\t\t}\n\t\t}\n\n\t\tfinal int numHeaders = getHeaderViewsCount();\n\t\tfinal int numFooters = getFooterViewsCount();\n\n\t\tboolean updated = false;\n\n\t\tint oldFirstExpPos = mFirstExpPos;\n\t\tint oldSecondExpPos = mSecondExpPos;\n\t\tfloat oldSlideFrac = mSlideFrac;\n\n\t\tif (mAnimate) {\n\t\t\tint edgeToEdge = Math.abs(edge - lastEdge);\n\n\t\t\tint edgeTop, edgeBottom;\n\t\t\tif (mFloatViewMid < edge) {\n\t\t\t\tedgeBottom = edge;\n\t\t\t\tedgeTop = lastEdge;\n\t\t\t} else {\n\t\t\t\tedgeTop = edge;\n\t\t\t\tedgeBottom = lastEdge;\n\t\t\t}\n\t\t\t// Log.d(\"mobeta\", \"edgeTop=\"+edgeTop+\" edgeBot=\"+edgeBottom);\n\n\t\t\tint slideRgnHeight = (int) (0.5f * mSlideRegionFrac * edgeToEdge);\n\t\t\tfloat slideRgnHeightF = (float) slideRgnHeight;\n\t\t\tint slideEdgeTop = edgeTop + slideRgnHeight;\n\t\t\tint slideEdgeBottom = edgeBottom - slideRgnHeight;\n\n\t\t\t// Three regions\n\t\t\tif (mFloatViewMid < slideEdgeTop) {\n\t\t\t\tmFirstExpPos = itemPos - 1;\n\t\t\t\tmSecondExpPos = itemPos;\n\t\t\t\tmSlideFrac = 0.5f * ((float) (slideEdgeTop - mFloatViewMid)) / slideRgnHeightF;\n\t\t\t\t// Log.d(\"mobeta\",\n\t\t\t\t// \"firstExp=\"+mFirstExpPos+\" secExp=\"+mSecondExpPos+\" slideFrac=\"+mSlideFrac);\n\t\t\t} else if (mFloatViewMid < slideEdgeBottom) {\n\t\t\t\tmFirstExpPos = itemPos;\n\t\t\t\tmSecondExpPos = itemPos;\n\t\t\t} else {\n\t\t\t\tmFirstExpPos = itemPos;\n\t\t\t\tmSecondExpPos = itemPos + 1;\n\t\t\t\tmSlideFrac = 0.5f * (1.0f + ((float) (edgeBottom - mFloatViewMid))\n\t\t\t\t\t\t/ slideRgnHeightF);\n\t\t\t\t// Log.d(\"mobeta\",\n\t\t\t\t// \"firstExp=\"+mFirstExpPos+\" secExp=\"+mSecondExpPos+\" slideFrac=\"+mSlideFrac);\n\t\t\t}\n\n\t\t} else {\n\t\t\tmFirstExpPos = itemPos;\n\t\t\tmSecondExpPos = itemPos;\n\t\t}\n\n\t\t// correct for headers and footers\n\t\tif (mFirstExpPos < numHeaders) {\n\t\t\titemPos = numHeaders;\n\t\t\tmFirstExpPos = itemPos;\n\t\t\tmSecondExpPos = itemPos;\n\t\t} else if (mSecondExpPos >= getCount() - numFooters) {\n\t\t\titemPos = getCount() - numFooters - 1;\n\t\t\tmFirstExpPos = itemPos;\n\t\t\tmSecondExpPos = itemPos;\n\t\t}\n\n\t\tif (mFirstExpPos != oldFirstExpPos || mSecondExpPos != oldSecondExpPos\n\t\t\t\t|| mSlideFrac != oldSlideFrac) {\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (itemPos != mFloatPos) {\n\t\t\tif (mDragListener != null) {\n\t\t\t\tmDragListener.drag(mFloatPos - numHeaders, itemPos - numHeaders);\n\t\t\t}\n\n\t\t\tmFloatPos = itemPos;\n\t\t\tupdated = true;\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t@Override\n\tprotected void onDraw(Canvas canvas) {\n\t\tsuper.onDraw(canvas);\n\n\t\tif (mTrackDragSort) {\n\t\t\tmDragSortTracker.appendState();\n\t\t}\n\t}\n\n\tprivate class SmoothAnimator implements Runnable {\n\t\tprotected long mStartTime;\n\n\t\tprivate float mDurationF;\n\n\t\tprivate float mAlpha;\n\t\tprivate float mA, mB, mC, mD;\n\n\t\tprivate boolean mCanceled;\n\n\t\tpublic SmoothAnimator(float smoothness, int duration) {\n\t\t\tmAlpha = smoothness;\n\t\t\tmDurationF = (float) duration;\n\t\t\tmA = mD = 1f / (2f * mAlpha * (1f - mAlpha));\n\t\t\tmB = mAlpha / (2f * (mAlpha - 1f));\n\t\t\tmC = 1f / (1f - mAlpha);\n\t\t}\n\n\t\tpublic float transform(float frac) {\n\t\t\tif (frac < mAlpha) {\n\t\t\t\treturn mA * frac * frac;\n\t\t\t} else if (frac < 1f - mAlpha) {\n\t\t\t\treturn mB + mC * frac;\n\t\t\t} else {\n\t\t\t\treturn 1f - mD * (frac - 1f) * (frac - 1f);\n\t\t\t}\n\t\t}\n\n\t\tpublic void start() {\n\t\t\tmStartTime = SystemClock.uptimeMillis();\n\t\t\tmCanceled = false;\n\t\t\tonStart();\n\t\t\tpost(this);\n\t\t}\n\n\t\tpublic void cancel() {\n\t\t\tmCanceled = true;\n\t\t}\n\n\t\tpublic void onStart() {\n\t\t\t// stub\n\t\t}\n\n\t\tpublic void onUpdate(float frac, float smoothFrac) {\n\t\t\t// stub\n\t\t}\n\n\t\tpublic void onStop() {\n\t\t\t// stub\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (mCanceled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfloat fraction = ((float) (SystemClock.uptimeMillis() - mStartTime)) / mDurationF;\n\n\t\t\tif (fraction >= 1f) {\n\t\t\t\tonUpdate(1f, 1f);\n\t\t\t\tonStop();\n\t\t\t} else {\n\t\t\t\tonUpdate(fraction, transform(fraction));\n\t\t\t\tpost(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Centers floating View under touch point.\n\t */\n\tprivate class LiftAnimator extends SmoothAnimator {\n\n\t\tprivate float mInitDragDeltaY;\n\t\tprivate float mFinalDragDeltaY;\n\n\t\tpublic LiftAnimator(float smoothness, int duration) {\n\t\t\tsuper(smoothness, duration);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onStart() {\n\t\t\tmInitDragDeltaY = mDragDeltaY;\n\t\t\tmFinalDragDeltaY = mFloatViewHeightHalf;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onUpdate(float frac, float smoothFrac) {\n\t\t\tif (mDragState != DRAGGING) {\n\t\t\t\tcancel();\n\t\t\t} else {\n\t\t\t\tmDragDeltaY = (int) (smoothFrac * mFinalDragDeltaY + (1f - smoothFrac)\n\t\t\t\t\t\t* mInitDragDeltaY);\n\t\t\t\tmFloatLoc.y = mY - mDragDeltaY;\n\t\t\t\tdoDragFloatView(true);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Centers floating View over drop slot before destroying.\n\t */\n\tprivate class DropAnimator extends SmoothAnimator {\n\n\t\tprivate int mDropPos;\n\t\tprivate int srcPos;\n\t\tprivate float mInitDeltaY;\n\t\tprivate float mInitDeltaX;\n\n\t\tpublic DropAnimator(float smoothness, int duration) {\n\t\t\tsuper(smoothness, duration);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onStart() {\n\t\t\tmDropPos = mFloatPos;\n\t\t\tsrcPos = mSrcPos;\n\t\t\tmDragState = DROPPING;\n\t\t\tmInitDeltaY = mFloatLoc.y - getTargetY();\n\t\t\tmInitDeltaX = mFloatLoc.x - getPaddingLeft();\n\t\t}\n\n\t\tprivate int getTargetY() {\n\t\t\tfinal int first = getFirstVisiblePosition();\n\t\t\tfinal int otherAdjust = (mItemHeightCollapsed + getDividerHeight()) / 2;\n\t\t\tView v = getChildAt(mDropPos - first);\n\t\t\tint targetY = -1;\n\t\t\tif (v != null) {\n\t\t\t\tif (mDropPos == srcPos) {\n\t\t\t\t\ttargetY = v.getTop();\n\t\t\t\t} else if (mDropPos < srcPos) {\n\t\t\t\t\t// expanded down\n\t\t\t\t\ttargetY = v.getTop() - otherAdjust;\n\t\t\t\t} else {\n\t\t\t\t\t// expanded up\n\t\t\t\t\ttargetY = v.getBottom() + otherAdjust - mFloatViewHeight;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// drop position is not on screen?? no animation\n\t\t\t\tcancel();\n\t\t\t}\n\n\t\t\treturn targetY;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onUpdate(float frac, float smoothFrac) {\n\t\t\tfinal int targetY = getTargetY();\n\t\t\tfinal int targetX = getPaddingLeft();\n\t\t\tfinal float deltaY = mFloatLoc.y - targetY;\n\t\t\tfinal float deltaX = mFloatLoc.x - targetX;\n\t\t\tfinal float f = 1f - smoothFrac;\n\t\t\tif (f < Math.abs(deltaY / mInitDeltaY) || f < Math.abs(deltaX / mInitDeltaX)) {\n\t\t\t\tmFloatLoc.y = targetY + (int) (mInitDeltaY * f);\n\t\t\t\tmFloatLoc.x = getPaddingLeft() + (int) (mInitDeltaX * f);\n\t\t\t\tdoDragFloatView(true);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onStop() {\n\t\t\tdropFloatView();\n\t\t}\n\n\t}\n\n\t/**\n\t * Collapses expanded items.\n\t */\n\tprivate class RemoveAnimator extends SmoothAnimator {\n\n\t\tprivate float mFloatLocX;\n\t\tprivate float mFirstStartBlank;\n\t\tprivate float mSecondStartBlank;\n\n\t\tprivate int mFirstChildHeight = -1;\n\t\tprivate int mSecondChildHeight = -1;\n\n\t\tprivate int mFirstPos;\n\t\tprivate int mSecondPos;\n\t\tprivate int srcPos;\n\n\t\tpublic RemoveAnimator(float smoothness, int duration) {\n\t\t\tsuper(smoothness, duration);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onStart() {\n\t\t\tmFirstChildHeight = -1;\n\t\t\tmSecondChildHeight = -1;\n\t\t\tmFirstPos = mFirstExpPos;\n\t\t\tmSecondPos = mSecondExpPos;\n\t\t\tsrcPos = mSrcPos;\n\t\t\tmDragState = REMOVING;\n\n\t\t\tmFloatLocX = mFloatLoc.x;\n\t\t\tif (mUseRemoveVelocity) {\n\t\t\t\tfloat minVelocity = 2f * getWidth();\n\t\t\t\tif (mRemoveVelocityX == 0) {\n\t\t\t\t\tmRemoveVelocityX = (mFloatLocX < 0 ? -1 : 1) * minVelocity;\n\t\t\t\t} else {\n\t\t\t\t\tminVelocity *= 2;\n\t\t\t\t\tif (mRemoveVelocityX < 0 && mRemoveVelocityX > -minVelocity)\n\t\t\t\t\t\tmRemoveVelocityX = -minVelocity;\n\t\t\t\t\telse if (mRemoveVelocityX > 0 && mRemoveVelocityX < minVelocity)\n\t\t\t\t\t\tmRemoveVelocityX = minVelocity;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdestroyFloatView();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onUpdate(float frac, float smoothFrac) {\n\t\t\tfloat f = 1f - smoothFrac;\n\n\t\t\tfinal int firstVis = getFirstVisiblePosition();\n\t\t\tView item = getChildAt(mFirstPos - firstVis);\n\t\t\tViewGroup.LayoutParams lp;\n\t\t\tint blank;\n\n\t\t\tif (mUseRemoveVelocity) {\n\t\t\t\tfloat dt = (float) (SystemClock.uptimeMillis() - mStartTime) / 1000;\n\t\t\t\tif (dt == 0)\n\t\t\t\t\treturn;\n\t\t\t\tfloat dx = mRemoveVelocityX * dt;\n\t\t\t\tint w = getWidth();\n\t\t\t\tmRemoveVelocityX += (mRemoveVelocityX > 0 ? 1 : -1) * dt * w;\n\t\t\t\tmFloatLocX += dx;\n\t\t\t\tmFloatLoc.x = (int) mFloatLocX;\n\t\t\t\tif (mFloatLocX < w && mFloatLocX > -w) {\n\t\t\t\t\tmStartTime = SystemClock.uptimeMillis();\n\t\t\t\t\tdoDragFloatView(true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (item != null) {\n\t\t\t\tif (mFirstChildHeight == -1) {\n\t\t\t\t\tmFirstChildHeight = getChildHeight(mFirstPos, item, false);\n\t\t\t\t\tmFirstStartBlank = (float) (item.getHeight() - mFirstChildHeight);\n\t\t\t\t}\n\t\t\t\tblank = Math.max((int) (f * mFirstStartBlank), 1);\n\t\t\t\tlp = item.getLayoutParams();\n\t\t\t\tlp.height = mFirstChildHeight + blank;\n\t\t\t\titem.setLayoutParams(lp);\n\t\t\t}\n\t\t\tif (mSecondPos != mFirstPos) {\n\t\t\t\titem = getChildAt(mSecondPos - firstVis);\n\t\t\t\tif (item != null) {\n\t\t\t\t\tif (mSecondChildHeight == -1) {\n\t\t\t\t\t\tmSecondChildHeight = getChildHeight(mSecondPos, item, false);\n\t\t\t\t\t\tmSecondStartBlank = (float) (item.getHeight() - mSecondChildHeight);\n\t\t\t\t\t}\n\t\t\t\t\tblank = Math.max((int) (f * mSecondStartBlank), 1);\n\t\t\t\t\tlp = item.getLayoutParams();\n\t\t\t\t\tlp.height = mSecondChildHeight + blank;\n\t\t\t\t\titem.setLayoutParams(lp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onStop() {\n\t\t\tdoRemoveItem();\n\t\t}\n\t}\n\n\tpublic void removeItem(int which) {\n\n\t\tmUseRemoveVelocity = false;\n\t\tremoveItem(which, 0);\n\t}\n\n\t/**\n\t * Removes an item from the list and animates the removal.\n\t *\n\t * @param which     Position to remove (NOTE: headers/footers ignored!\n\t *                  this is a position in your input ListAdapter).\n\t * @param velocityX\n\t */\n\tpublic void removeItem(int which, float velocityX) {\n\t\tif (mDragState == IDLE || mDragState == DRAGGING) {\n\n\t\t\tif (mDragState == IDLE) {\n\t\t\t\t// called from outside drag-sort\n\t\t\t\tmSrcPos = getHeaderViewsCount() + which;\n\t\t\t\tmFirstExpPos = mSrcPos;\n\t\t\t\tmSecondExpPos = mSrcPos;\n\t\t\t\tmFloatPos = mSrcPos;\n\t\t\t\tView v = getChildAt(mSrcPos - getFirstVisiblePosition());\n\t\t\t\tif (v != null) {\n\t\t\t\t\tv.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmDragState = REMOVING;\n\t\t\tmRemoveVelocityX = velocityX;\n\n\t\t\tif (mInTouchEvent) {\n\t\t\t\tswitch (mCancelMethod) {\n\t\t\t\t\tcase ON_TOUCH_EVENT:\n\t\t\t\t\t\tsuper.onTouchEvent(mCancelEvent);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ON_INTERCEPT_TOUCH_EVENT:\n\t\t\t\t\t\tsuper.onInterceptTouchEvent(mCancelEvent);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mRemoveAnimator != null) {\n\t\t\t\tmRemoveAnimator.start();\n\t\t\t} else {\n\t\t\t\tdoRemoveItem(which);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Move an item, bypassing the drag-sort process. Simply calls\n\t * through to {@link DropListener#drop(int, int)}.\n\t *\n\t * @param from Position to move (NOTE: headers/footers ignored!\n\t *             this is a position in your input ListAdapter).\n\t * @param to   Target position (NOTE: headers/footers ignored!\n\t *             this is a position in your input ListAdapter).\n\t */\n\tpublic void moveItem(int from, int to) {\n\t\tif (mDropListener != null) {\n\t\t\tfinal int count = getInputAdapter().getCount();\n\t\t\tif (from >= 0 && from < count && to >= 0 && to < count) {\n\t\t\t\tmDropListener.drop(from, to);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Cancel a drag. Calls {@link #stopDrag(boolean, boolean)} with\n\t * <code>true</code> as the first argument.\n\t */\n\tpublic void cancelDrag() {\n\t\tif (mDragState == DRAGGING) {\n\t\t\tmDragScroller.stopScrolling(true);\n\t\t\tdestroyFloatView();\n\t\t\tclearPositions();\n\t\t\tadjustAllItems();\n\n\t\t\tif (mInTouchEvent) {\n\t\t\t\tmDragState = STOPPED;\n\t\t\t} else {\n\t\t\t\tmDragState = IDLE;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void clearPositions() {\n\t\tmSrcPos = -1;\n\t\tmFirstExpPos = -1;\n\t\tmSecondExpPos = -1;\n\t\tmFloatPos = -1;\n\t}\n\n\tprivate void dropFloatView() {\n\t\t// must set to avoid cancelDrag being called from the\n\t\t// DataSetObserver\n\t\tmDragState = DROPPING;\n\n\t\tif (mDropListener != null && mFloatPos >= 0 && mFloatPos < getCount()) {\n\t\t\tfinal int numHeaders = getHeaderViewsCount();\n\t\t\tmDropListener.drop(mSrcPos - numHeaders, mFloatPos - numHeaders);\n\t\t}\n\n\t\tdestroyFloatView();\n\n\t\tadjustOnReorder();\n\t\tclearPositions();\n\t\tadjustAllItems();\n\n\t\t// now the drag is done\n\t\tif (mInTouchEvent) {\n\t\t\tmDragState = STOPPED;\n\t\t} else {\n\t\t\tmDragState = IDLE;\n\t\t}\n\t}\n\n\tprivate void doRemoveItem() {\n\t\tdoRemoveItem(mSrcPos - getHeaderViewsCount());\n\t}\n\n\t/**\n\t * Removes dragged item from the list. Calls RemoveListener.\n\t */\n\tprivate void doRemoveItem(int which) {\n\t\t// must set to avoid cancelDrag being called from the\n\t\t// DataSetObserver\n\t\tmDragState = REMOVING;\n\n\t\t// end it\n\t\tif (mRemoveListener != null) {\n\t\t\tmRemoveListener.remove(which);\n\t\t}\n\n\t\tdestroyFloatView();\n\n\t\tadjustOnReorder();\n\t\tclearPositions();\n\n\t\t// now the drag is done\n\t\tif (mInTouchEvent) {\n\t\t\tmDragState = STOPPED;\n\t\t} else {\n\t\t\tmDragState = IDLE;\n\t\t}\n\t}\n\n\tprivate void adjustOnReorder() {\n\t\tfinal int firstPos = getFirstVisiblePosition();\n\t\t// Log.d(\"mobeta\", \"first=\"+firstPos+\" src=\"+mSrcPos);\n\t\tif (mSrcPos < firstPos) {\n\t\t\t// collapsed src item is off screen;\n\t\t\t// adjust the scroll after item heights have been fixed\n\t\t\tView v = getChildAt(0);\n\t\t\tint top = 0;\n\t\t\tif (v != null) {\n\t\t\t\ttop = v.getTop();\n\t\t\t}\n\t\t\t// Log.d(\"mobeta\", \"top=\"+top+\" fvh=\"+mFloatViewHeight);\n\t\t\tsetSelectionFromTop(firstPos - 1, top - getPaddingTop());\n\t\t}\n\t}\n\n\t/**\n\t * Stop a drag in progress. Pass <code>true</code> if you would\n\t * like to remove the dragged item from the list.\n\t *\n\t * @param remove Remove the dragged item from the list. Calls\n\t *               a registered RemoveListener, if one exists. Otherwise, calls\n\t *               the DropListener, if one exists.\n\t * @return True if the stop was successful. False if there is\n\t * no floating View.\n\t */\n\tpublic boolean stopDrag(boolean remove) {\n\t\tmUseRemoveVelocity = false;\n\t\treturn stopDrag(remove, 0);\n\t}\n\n\tpublic boolean stopDragWithVelocity(boolean remove, float velocityX) {\n\n\t\tmUseRemoveVelocity = true;\n\t\treturn stopDrag(remove, velocityX);\n\t}\n\n\tpublic boolean stopDrag(boolean remove, float velocityX) {\n\t\tif (mFloatView != null) {\n\t\t\tmDragScroller.stopScrolling(true);\n\n\t\t\tif (remove) {\n\t\t\t\tremoveItem(mSrcPos - getHeaderViewsCount(), velocityX);\n\t\t\t} else {\n\t\t\t\tif (mDropAnimator != null) {\n\t\t\t\t\tmDropAnimator.start();\n\t\t\t\t} else {\n\t\t\t\t\tdropFloatView();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mTrackDragSort) {\n\t\t\t\tmDragSortTracker.stopTracking();\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\t// stop failed\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean onTouchEvent(MotionEvent ev) {\n\t\tif (mIgnoreTouchEvent) {\n\t\t\tmIgnoreTouchEvent = false;\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!mDragEnabled) {\n\t\t\treturn super.onTouchEvent(ev);\n\t\t}\n\n\t\tboolean more = false;\n\n\t\tboolean lastCallWasIntercept = mLastCallWasIntercept;\n\t\tmLastCallWasIntercept = false;\n\n\t\tif (!lastCallWasIntercept) {\n\t\t\tsaveTouchCoords(ev);\n\t\t}\n\n\t\t// if (mFloatView != null) {\n\t\tif (mDragState == DRAGGING) {\n\t\t\tonDragTouchEvent(ev);\n\t\t\tmore = true; // give us more!\n\t\t} else {\n\t\t\t// what if float view is null b/c we dropped in middle\n\t\t\t// of drag touch event?\n\n\t\t\t// if (mDragState != STOPPED) {\n\t\t\tif (mDragState == IDLE) {\n\t\t\t\tif (super.onTouchEvent(ev)) {\n\t\t\t\t\tmore = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint action = ev.getAction() & MotionEvent.ACTION_MASK;\n\n\t\t\tswitch (action) {\n\t\t\t\tcase MotionEvent.ACTION_CANCEL:\n\t\t\t\tcase MotionEvent.ACTION_UP:\n\t\t\t\t\tdoActionUpOrCancel();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (more) {\n\t\t\t\t\t\tmCancelMethod = ON_TOUCH_EVENT;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn more;\n\t}\n\n\tprivate void doActionUpOrCancel() {\n\t\tmCancelMethod = NO_CANCEL;\n\t\tmInTouchEvent = false;\n\t\tif (mDragState == STOPPED) {\n\t\t\tmDragState = IDLE;\n\t\t}\n\t\tmCurrFloatAlpha = mFloatAlpha;\n\t\tmListViewIntercepted = false;\n\t\tmChildHeightCache.clear();\n\t}\n\n\tprivate void saveTouchCoords(MotionEvent ev) {\n\t\tint action = ev.getAction() & MotionEvent.ACTION_MASK;\n\t\tif (action != MotionEvent.ACTION_DOWN) {\n\t\t\tmLastX = mX;\n\t\t\tmLastY = mY;\n\t\t}\n\t\tmX = (int) ev.getX();\n\t\tmY = (int) ev.getY();\n\t\tif (action == MotionEvent.ACTION_DOWN) {\n\t\t\tmLastX = mX;\n\t\t\tmLastY = mY;\n\t\t}\n\t\tmOffsetX = (int) ev.getRawX() - mX;\n\t\tmOffsetY = (int) ev.getRawY() - mY;\n\t}\n\n\tpublic boolean listViewIntercepted() {\n\t\treturn mListViewIntercepted;\n\t}\n\n\tprivate boolean mListViewIntercepted = false;\n\n\t@Override\n\tpublic boolean onInterceptTouchEvent(MotionEvent ev) {\n\t\tif (!mDragEnabled) {\n\t\t\treturn super.onInterceptTouchEvent(ev);\n\t\t}\n\n\t\tsaveTouchCoords(ev);\n\t\tmLastCallWasIntercept = true;\n\n\t\tint action = ev.getAction() & MotionEvent.ACTION_MASK;\n\n\t\tif (action == MotionEvent.ACTION_DOWN) {\n\t\t\tif (mDragState != IDLE) {\n\t\t\t\t// intercept and ignore\n\t\t\t\tmIgnoreTouchEvent = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tmInTouchEvent = true;\n\t\t}\n\n\t\tboolean intercept = false;\n\n\t\t// the following deals with calls to super.onInterceptTouchEvent\n\t\tif (mFloatView != null) {\n\t\t\t// super's touch event canceled in startDrag\n\t\t\tintercept = true;\n\t\t} else {\n\t\t\tif (super.onInterceptTouchEvent(ev)) {\n\t\t\t\tmListViewIntercepted = true;\n\t\t\t\tintercept = true;\n\t\t\t}\n\n\t\t\tswitch (action) {\n\t\t\t\tcase MotionEvent.ACTION_CANCEL:\n\t\t\t\tcase MotionEvent.ACTION_UP:\n\t\t\t\t\tdoActionUpOrCancel();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (intercept) {\n\t\t\t\t\t\tmCancelMethod = ON_TOUCH_EVENT;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmCancelMethod = ON_INTERCEPT_TOUCH_EVENT;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {\n\t\t\tmInTouchEvent = false;\n\t\t}\n\n\t\treturn intercept;\n\t}\n\n\t/**\n\t * Set the width of each drag scroll region by specifying\n\t * a fraction of the ListView height.\n\t *\n\t * @param heightFraction Fraction of ListView height. Capped at\n\t *                       0.5f.\n\t */\n\tpublic void setDragScrollStart(float heightFraction) {\n\t\tsetDragScrollStarts(heightFraction, heightFraction);\n\t}\n\n\t/**\n\t * Set the width of each drag scroll region by specifying\n\t * a fraction of the ListView height.\n\t *\n\t * @param upperFrac Fraction of ListView height for up-scroll bound.\n\t *                  Capped at 0.5f.\n\t * @param lowerFrac Fraction of ListView height for down-scroll bound.\n\t *                  Capped at 0.5f.\n\t */\n\tpublic void setDragScrollStarts(float upperFrac, float lowerFrac) {\n\t\tif (lowerFrac > 0.5f) {\n\t\t\tmDragDownScrollStartFrac = 0.5f;\n\t\t} else {\n\t\t\tmDragDownScrollStartFrac = lowerFrac;\n\t\t}\n\n\t\tif (upperFrac > 0.5f) {\n\t\t\tmDragUpScrollStartFrac = 0.5f;\n\t\t} else {\n\t\t\tmDragUpScrollStartFrac = upperFrac;\n\t\t}\n\n\t\tif (getHeight() != 0) {\n\t\t\tupdateScrollStarts();\n\t\t}\n\t}\n\n\tprivate void continueDrag(int x, int y) {\n\n\t\t// proposed position\n\t\tmFloatLoc.x = x - mDragDeltaX;\n\t\tmFloatLoc.y = y - mDragDeltaY;\n\n\t\tdoDragFloatView(true);\n\n\t\tint minY = Math.min(y, mFloatViewMid + mFloatViewHeightHalf);\n\t\tint maxY = Math.max(y, mFloatViewMid - mFloatViewHeightHalf);\n\n\t\t// get the current scroll direction\n\t\tint currentScrollDir = mDragScroller.getScrollDir();\n\n\t\tif (minY > mLastY && minY > mDownScrollStartY && currentScrollDir != DragScroller.DOWN) {\n\t\t\t// dragged down, it is below the down scroll start and it is not\n\t\t\t// scrolling up\n\n\t\t\tif (currentScrollDir != DragScroller.STOP) {\n\t\t\t\t// moved directly from up scroll to down scroll\n\t\t\t\tmDragScroller.stopScrolling(true);\n\t\t\t}\n\n\t\t\t// start scrolling down\n\t\t\tmDragScroller.startScrolling(DragScroller.DOWN);\n\t\t} else if (maxY < mLastY && maxY < mUpScrollStartY && currentScrollDir != DragScroller.UP) {\n\t\t\t// dragged up, it is above the up scroll start and it is not\n\t\t\t// scrolling up\n\n\t\t\tif (currentScrollDir != DragScroller.STOP) {\n\t\t\t\t// moved directly from down scroll to up scroll\n\t\t\t\tmDragScroller.stopScrolling(true);\n\t\t\t}\n\n\t\t\t// start scrolling up\n\t\t\tmDragScroller.startScrolling(DragScroller.UP);\n\t\t} else if (maxY >= mUpScrollStartY && minY <= mDownScrollStartY\n\t\t\t\t&& mDragScroller.isScrolling()) {\n\t\t\t// not in the upper nor in the lower drag-scroll regions but it is\n\t\t\t// still scrolling\n\n\t\t\tmDragScroller.stopScrolling(true);\n\t\t}\n\t}\n\n\tprivate void updateScrollStarts() {\n\t\tfinal int padTop = getPaddingTop();\n\t\tfinal int listHeight = getHeight() - padTop - getPaddingBottom();\n\t\tfloat heightF = (float) listHeight;\n\n\t\tmUpScrollStartYF = padTop + mDragUpScrollStartFrac * heightF;\n\t\tmDownScrollStartYF = padTop + (1.0f - mDragDownScrollStartFrac) * heightF;\n\n\t\tmUpScrollStartY = (int) mUpScrollStartYF;\n\t\tmDownScrollStartY = (int) mDownScrollStartYF;\n\n\t\tmDragUpScrollHeight = mUpScrollStartYF - padTop;\n\t\tmDragDownScrollHeight = padTop + listHeight - mDownScrollStartYF;\n\t}\n\n\t@Override\n\tprotected void onSizeChanged(int w, int h, int oldw, int oldh) {\n\t\tsuper.onSizeChanged(w, h, oldw, oldh);\n\t\tupdateScrollStarts();\n\t}\n\n\tprivate void adjustAllItems() {\n\t\tfinal int first = getFirstVisiblePosition();\n\t\tfinal int last = getLastVisiblePosition();\n\n\t\tint begin = Math.max(0, getHeaderViewsCount() - first);\n\t\tint end = Math.min(last - first, getCount() - 1 - getFooterViewsCount() - first);\n\n\t\tfor (int i = begin; i <= end; ++i) {\n\t\t\tView v = getChildAt(i);\n\t\t\tif (v != null) {\n\t\t\t\tadjustItem(first + i, v, false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void adjustItem(int position) {\n\t\tView v = getChildAt(position - getFirstVisiblePosition());\n\n\t\tif (v != null) {\n\t\t\tadjustItem(position, v, false);\n\t\t}\n\t}\n\n\t/**\n\t * Sets layout param height, gravity, and visibility  on\n\t * wrapped item.\n\t */\n\tprivate void adjustItem(int position, View v, boolean invalidChildHeight) {\n\n\t\t// Adjust item height\n\t\tViewGroup.LayoutParams lp = v.getLayoutParams();\n\t\tint height;\n\t\tif (position != mSrcPos && position != mFirstExpPos && position != mSecondExpPos) {\n\t\t\theight = ViewGroup.LayoutParams.WRAP_CONTENT;\n\t\t} else {\n\t\t\theight = calcItemHeight(position, v, invalidChildHeight);\n\t\t}\n\n\t\tif (height != lp.height) {\n\t\t\tlp.height = height;\n\t\t\tv.setLayoutParams(lp);\n\t\t}\n\n\t\t// Adjust item gravity\n\t\tif (position == mFirstExpPos || position == mSecondExpPos) {\n\t\t\tif (position < mSrcPos) {\n\t\t\t\t((DragSortItemView) v).setGravity(Gravity.BOTTOM);\n\t\t\t} else if (position > mSrcPos) {\n\t\t\t\t((DragSortItemView) v).setGravity(Gravity.TOP);\n\t\t\t}\n\t\t}\n\n\t\t// Finally adjust item visibility\n\n\t\tint oldVis = v.getVisibility();\n\t\tint vis = View.VISIBLE;\n\n\t\tif (position == mSrcPos && mFloatView != null) {\n\t\t\tvis = View.INVISIBLE;\n\t\t}\n\n\t\tif (vis != oldVis) {\n\t\t\tv.setVisibility(vis);\n\t\t}\n\t}\n\n\tprivate int getChildHeight(int position) {\n\t\tif (position == mSrcPos) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tView v = getChildAt(position - getFirstVisiblePosition());\n\n\t\tif (v != null) {\n\t\t\t// item is onscreen, therefore child height is valid,\n\t\t\t// hence the \"true\"\n\t\t\treturn getChildHeight(position, v, false);\n\t\t} else {\n\t\t\t// item is offscreen\n\t\t\t// first check cache for child height at this position\n\t\t\tint childHeight = mChildHeightCache.get(position);\n\t\t\tif (childHeight != -1) {\n\t\t\t\t// Log.d(\"mobeta\", \"found child height in cache!\");\n\t\t\t\treturn childHeight;\n\t\t\t}\n\n\t\t\tfinal ListAdapter adapter = getAdapter();\n\t\t\tint type = adapter.getItemViewType(position);\n\n\t\t\t// There might be a better place for checking for the following\n\t\t\tfinal int typeCount = adapter.getViewTypeCount();\n\t\t\tif (typeCount != mSampleViewTypes.length) {\n\t\t\t\tmSampleViewTypes = new View[typeCount];\n\t\t\t}\n\n\t\t\tif (type >= 0) {\n\t\t\t\tif (mSampleViewTypes[type] == null) {\n\t\t\t\t\tv = adapter.getView(position, null, this);\n\t\t\t\t\tmSampleViewTypes[type] = v;\n\t\t\t\t} else {\n\t\t\t\t\tv = adapter.getView(position, mSampleViewTypes[type], this);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// type is HEADER_OR_FOOTER or IGNORE\n\t\t\t\tv = adapter.getView(position, null, this);\n\t\t\t}\n\n\t\t\t// current child height is invalid, hence \"true\" below\n\t\t\tchildHeight = getChildHeight(position, v, true);\n\n\t\t\t// cache it because this could have been expensive\n\t\t\tmChildHeightCache.add(position, childHeight);\n\n\t\t\treturn childHeight;\n\t\t}\n\t}\n\n\tprivate int getChildHeight(int position, View item, boolean invalidChildHeight) {\n\t\tif (position == mSrcPos) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tView child;\n\t\tif (position < getHeaderViewsCount() || position >= getCount() - getFooterViewsCount()) {\n\t\t\tchild = item;\n\t\t} else {\n\t\t\tchild = ((ViewGroup) item).getChildAt(0);\n\t\t}\n\n\t\tViewGroup.LayoutParams lp = child.getLayoutParams();\n\n\t\tif (lp != null) {\n\t\t\tif (lp.height > 0) {\n\t\t\t\treturn lp.height;\n\t\t\t}\n\t\t}\n\n\t\tint childHeight = child.getHeight();\n\n\t\tif (childHeight == 0 || invalidChildHeight) {\n\t\t\tmeasureItem(child);\n\t\t\tchildHeight = child.getMeasuredHeight();\n\t\t}\n\n\t\treturn childHeight;\n\t}\n\n\tprivate int calcItemHeight(int position, View item, boolean invalidChildHeight) {\n\t\treturn calcItemHeight(position, getChildHeight(position, item, invalidChildHeight));\n\t}\n\n\tprivate int calcItemHeight(int position, int childHeight) {\n\n\t\tint divHeight = getDividerHeight();\n\n\t\tboolean isSliding = mAnimate && mFirstExpPos != mSecondExpPos;\n\t\tint maxNonSrcBlankHeight = mFloatViewHeight - mItemHeightCollapsed;\n\t\tint slideHeight = (int) (mSlideFrac * maxNonSrcBlankHeight);\n\n\t\tint height;\n\n\t\tif (position == mSrcPos) {\n\t\t\tif (mSrcPos == mFirstExpPos) {\n\t\t\t\tif (isSliding) {\n\t\t\t\t\theight = slideHeight + mItemHeightCollapsed;\n\t\t\t\t} else {\n\t\t\t\t\theight = mFloatViewHeight;\n\t\t\t\t}\n\t\t\t} else if (mSrcPos == mSecondExpPos) {\n\t\t\t\t// if gets here, we know an item is sliding\n\t\t\t\theight = mFloatViewHeight - slideHeight;\n\t\t\t} else {\n\t\t\t\theight = mItemHeightCollapsed;\n\t\t\t}\n\t\t} else if (position == mFirstExpPos) {\n\t\t\tif (isSliding) {\n\t\t\t\theight = childHeight + slideHeight;\n\t\t\t} else {\n\t\t\t\theight = childHeight + maxNonSrcBlankHeight;\n\t\t\t}\n\t\t} else if (position == mSecondExpPos) {\n\t\t\t// we know an item is sliding (b/c 2ndPos != 1stPos)\n\t\t\theight = childHeight + maxNonSrcBlankHeight - slideHeight;\n\t\t} else {\n\t\t\theight = childHeight;\n\t\t}\n\n\t\treturn height;\n\t}\n\n\t@Override\n\tpublic void requestLayout() {\n\t\tif (!mBlockLayoutRequests) {\n\t\t\tsuper.requestLayout();\n\t\t}\n\t}\n\n\tprivate int adjustScroll(int movePos, View moveItem, int oldFirstExpPos, int oldSecondExpPos) {\n\t\tint adjust = 0;\n\n\t\tfinal int childHeight = getChildHeight(movePos);\n\n\t\tint moveHeightBefore = moveItem.getHeight();\n\t\tint moveHeightAfter = calcItemHeight(movePos, childHeight);\n\n\t\tint moveBlankBefore = moveHeightBefore;\n\t\tint moveBlankAfter = moveHeightAfter;\n\t\tif (movePos != mSrcPos) {\n\t\t\tmoveBlankBefore -= childHeight;\n\t\t\tmoveBlankAfter -= childHeight;\n\t\t}\n\n\t\tint maxBlank = mFloatViewHeight;\n\t\tif (mSrcPos != mFirstExpPos && mSrcPos != mSecondExpPos) {\n\t\t\tmaxBlank -= mItemHeightCollapsed;\n\t\t}\n\n\t\tif (movePos <= oldFirstExpPos) {\n\t\t\tif (movePos > mFirstExpPos) {\n\t\t\t\tadjust += maxBlank - moveBlankAfter;\n\t\t\t}\n\t\t} else if (movePos == oldSecondExpPos) {\n\t\t\tif (movePos <= mFirstExpPos) {\n\t\t\t\tadjust += moveBlankBefore - maxBlank;\n\t\t\t} else if (movePos == mSecondExpPos) {\n\t\t\t\tadjust += moveHeightBefore - moveHeightAfter;\n\t\t\t} else {\n\t\t\t\tadjust += moveBlankBefore;\n\t\t\t}\n\t\t} else {\n\t\t\tif (movePos <= mFirstExpPos) {\n\t\t\t\tadjust -= maxBlank;\n\t\t\t} else if (movePos == mSecondExpPos) {\n\t\t\t\tadjust -= moveBlankAfter;\n\t\t\t}\n\t\t}\n\n\t\treturn adjust;\n\t}\n\n\tprivate void measureItem(View item) {\n\t\tViewGroup.LayoutParams lp = item.getLayoutParams();\n\t\tif (lp == null) {\n\t\t\tlp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n\t\t\titem.setLayoutParams(lp);\n\t\t}\n\t\tint wspec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, getListPaddingLeft()\n\t\t\t\t+ getListPaddingRight(), lp.width);\n\t\tint hspec;\n\t\tif (lp.height > 0) {\n\t\t\thspec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);\n\t\t} else {\n\t\t\thspec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);\n\t\t}\n\t\titem.measure(wspec, hspec);\n\t}\n\n\tprivate void measureFloatView() {\n\t\tif (mFloatView != null) {\n\t\t\tmeasureItem(mFloatView);\n\t\t\tmFloatViewHeight = mFloatView.getMeasuredHeight();\n\t\t\tmFloatViewHeightHalf = mFloatViewHeight / 2;\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\tsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\t\t// Log.d(\"mobeta\", \"onMeasure called\");\n\t\tif (mFloatView != null) {\n\t\t\tif (mFloatView.isLayoutRequested()) {\n\t\t\t\tmeasureFloatView();\n\t\t\t}\n\t\t\tmFloatViewOnMeasured = true; // set to false after layout\n\t\t}\n\t\tmWidthMeasureSpec = widthMeasureSpec;\n\t}\n\n\t@Override\n\tprotected void layoutChildren() {\n\t\tsuper.layoutChildren();\n\n\t\tif (mFloatView != null) {\n\t\t\tif (mFloatView.isLayoutRequested() && !mFloatViewOnMeasured) {\n\t\t\t\t// Have to measure here when usual android measure\n\t\t\t\t// pass is skipped. This happens during a drag-sort\n\t\t\t\t// when layoutChildren is called directly.\n\t\t\t\tmeasureFloatView();\n\t\t\t}\n\t\t\tmFloatView.layout(0, 0, mFloatView.getMeasuredWidth(), mFloatView.getMeasuredHeight());\n\t\t\tmFloatViewOnMeasured = false;\n\t\t}\n\t}\n\n\tprotected boolean onDragTouchEvent(MotionEvent ev) {\n\t\t// we are in a drag\n\t\tint action = ev.getAction() & MotionEvent.ACTION_MASK;\n\n\t\tswitch (ev.getAction() & MotionEvent.ACTION_MASK) {\n\t\t\tcase MotionEvent.ACTION_CANCEL:\n\t\t\t\tif (mDragState == DRAGGING) {\n\t\t\t\t\tcancelDrag();\n\t\t\t\t}\n\t\t\t\tdoActionUpOrCancel();\n\t\t\t\tbreak;\n\t\t\tcase MotionEvent.ACTION_UP:\n\t\t\t\t// Log.d(\"mobeta\", \"calling stopDrag from onDragTouchEvent\");\n\t\t\t\tif (mDragState == DRAGGING) {\n\t\t\t\t\tstopDrag(false);\n\t\t\t\t}\n\t\t\t\tdoActionUpOrCancel();\n\t\t\t\tbreak;\n\t\t\tcase MotionEvent.ACTION_MOVE:\n\t\t\t\tcontinueDrag((int) ev.getX(), (int) ev.getY());\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate boolean mFloatViewInvalidated = false;\n\n\tprivate void invalidateFloatView() {\n\t\tmFloatViewInvalidated = true;\n\t}\n\n\t/**\n\t * Start a drag of item at <code>position</code> using the\n\t * registered FloatViewManager. Calls through\n\t * to {@link #startDrag(int, View, int, int, int)} after obtaining\n\t * the floating View from the FloatViewManager.\n\t *\n\t * @param position  Item to drag.\n\t * @param dragFlags Flags that restrict some movements of the\n\t *                  floating View. For example, set <code>dragFlags |=\n\t *                  ~{@link #DRAG_NEG_X}</code> to allow dragging the floating\n\t *                  View in all directions except off the screen to the left.\n\t * @param deltaX    Offset in x of the touch coordinate from the\n\t *                  left edge of the floating View (i.e. touch-x minus float View\n\t *                  left).\n\t * @param deltaY    Offset in y of the touch coordinate from the\n\t *                  top edge of the floating View (i.e. touch-y minus float View\n\t *                  top).\n\t * @return True if the drag was started, false otherwise. This\n\t * <code>startDrag</code> will fail if we are not currently in\n\t * a touch event, there is no registered FloatViewManager,\n\t * or the FloatViewManager returns a null View.\n\t */\n\tpublic boolean startDrag(int position, int dragFlags, int deltaX, int deltaY) {\n\t\tif (!mInTouchEvent || mFloatViewManager == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tView v = mFloatViewManager.onCreateFloatView(position);\n\n\t\tif (v == null) {\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn startDrag(position, v, dragFlags, deltaX, deltaY);\n\t\t}\n\n\t}\n\n\t/**\n\t * Start a drag of item at <code>position</code> without using\n\t * a FloatViewManager.\n\t *\n\t * @param position  Item to drag.\n\t * @param floatView Floating View.\n\t * @param dragFlags Flags that restrict some movements of the\n\t *                  floating View. For example, set <code>dragFlags |=\n\t *                  ~{@link #DRAG_NEG_X}</code> to allow dragging the floating\n\t *                  View in all directions except off the screen to the left.\n\t * @param deltaX    Offset in x of the touch coordinate from the\n\t *                  left edge of the floating View (i.e. touch-x minus float View\n\t *                  left).\n\t * @param deltaY    Offset in y of the touch coordinate from the\n\t *                  top edge of the floating View (i.e. touch-y minus float View\n\t *                  top).\n\t * @return True if the drag was started, false otherwise. This\n\t * <code>startDrag</code> will fail if we are not currently in\n\t * a touch event, <code>floatView</code> is null, or there is\n\t * a drag in progress.\n\t */\n\tpublic boolean startDrag(int position, View floatView, int dragFlags, int deltaX, int deltaY) {\n\t\tif (mDragState != IDLE || !mInTouchEvent || mFloatView != null || floatView == null\n\t\t\t\t|| !mDragEnabled) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (getParent() != null) {\n\t\t\tgetParent().requestDisallowInterceptTouchEvent(true);\n\t\t}\n\n\t\tint pos = position + getHeaderViewsCount();\n\t\tmFirstExpPos = pos;\n\t\tmSecondExpPos = pos;\n\t\tmSrcPos = pos;\n\t\tmFloatPos = pos;\n\n\t\t// mDragState = dragType;\n\t\tmDragState = DRAGGING;\n\t\tmDragFlags = 0;\n\t\tmDragFlags |= dragFlags;\n\n\t\tmFloatView = floatView;\n\t\tmeasureFloatView(); // sets mFloatViewHeight\n\n\t\tmDragDeltaX = deltaX;\n\t\tmDragDeltaY = deltaY;\n\t\tmDragStartY = mY;\n\n\t\t// updateFloatView(mX - mDragDeltaX, mY - mDragDeltaY);\n\t\tmFloatLoc.x = mX - mDragDeltaX;\n\t\tmFloatLoc.y = mY - mDragDeltaY;\n\n\t\t// set src item invisible\n\t\tfinal View srcItem = getChildAt(mSrcPos - getFirstVisiblePosition());\n\n\t\tif (srcItem != null) {\n\t\t\tsrcItem.setVisibility(View.INVISIBLE);\n\t\t}\n\n\t\tif (mTrackDragSort) {\n\t\t\tmDragSortTracker.startTracking();\n\t\t}\n\n\t\t// once float view is created, events are no longer passed\n\t\t// to ListView\n\t\tswitch (mCancelMethod) {\n\t\t\tcase ON_TOUCH_EVENT:\n\t\t\t\tsuper.onTouchEvent(mCancelEvent);\n\t\t\t\tbreak;\n\t\t\tcase ON_INTERCEPT_TOUCH_EVENT:\n\t\t\t\tsuper.onInterceptTouchEvent(mCancelEvent);\n\t\t\t\tbreak;\n\t\t}\n\n\t\trequestLayout();\n\n\t\tif (mLiftAnimator != null) {\n\t\t\tmLiftAnimator.start();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate void doDragFloatView(boolean forceInvalidate) {\n\t\tint movePos = getFirstVisiblePosition() + getChildCount() / 2;\n\t\tView moveItem = getChildAt(getChildCount() / 2);\n\n\t\tif (moveItem == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tdoDragFloatView(movePos, moveItem, forceInvalidate);\n\t}\n\n\tprivate void doDragFloatView(int movePos, View moveItem, boolean forceInvalidate) {\n\t\tmBlockLayoutRequests = true;\n\n\t\tupdateFloatView();\n\n\t\tint oldFirstExpPos = mFirstExpPos;\n\t\tint oldSecondExpPos = mSecondExpPos;\n\n\t\tboolean updated = updatePositions();\n\n\t\tif (updated) {\n\t\t\tadjustAllItems();\n\t\t\tint scroll = adjustScroll(movePos, moveItem, oldFirstExpPos, oldSecondExpPos);\n\t\t\t// Log.d(\"mobeta\", \"  adjust scroll=\"+scroll);\n\n\t\t\tsetSelectionFromTop(movePos, moveItem.getTop() + scroll - getPaddingTop());\n\t\t\tlayoutChildren();\n\t\t}\n\n\t\tif (updated || forceInvalidate) {\n\t\t\tinvalidate();\n\t\t}\n\n\t\tmBlockLayoutRequests = false;\n\t}\n\n\t/**\n\t * Sets float View location based on suggested values and\n\t * constraints set in mDragFlags.\n\t */\n\tprivate void updateFloatView() {\n\n\t\tif (mFloatViewManager != null) {\n\t\t\tmTouchLoc.set(mX, mY);\n\t\t\tmFloatViewManager.onDragFloatView(mFloatView, mFloatLoc, mTouchLoc);\n\t\t}\n\n\t\tfinal int floatX = mFloatLoc.x;\n\t\tfinal int floatY = mFloatLoc.y;\n\n\t\t// restrict x motion\n\t\tint padLeft = getPaddingLeft();\n\t\tif ((mDragFlags & DRAG_POS_X) == 0 && floatX > padLeft) {\n\t\t\tmFloatLoc.x = padLeft;\n\t\t} else if ((mDragFlags & DRAG_NEG_X) == 0 && floatX < padLeft) {\n\t\t\tmFloatLoc.x = padLeft;\n\t\t}\n\n\t\t// keep floating view from going past bottom of last header view\n\t\tfinal int numHeaders = getHeaderViewsCount();\n\t\tfinal int numFooters = getFooterViewsCount();\n\t\tfinal int firstPos = getFirstVisiblePosition();\n\t\tfinal int lastPos = getLastVisiblePosition();\n\n\t\t// Log.d(\"mobeta\",\n\t\t// \"nHead=\"+numHeaders+\" nFoot=\"+numFooters+\" first=\"+firstPos+\" last=\"+lastPos);\n\t\tint topLimit = getPaddingTop();\n\t\tif (firstPos < numHeaders) {\n\t\t\ttopLimit = getChildAt(numHeaders - firstPos - 1).getBottom();\n\t\t}\n\t\tif ((mDragFlags & DRAG_NEG_Y) == 0) {\n\t\t\tif (firstPos <= mSrcPos) {\n\t\t\t\ttopLimit = Math.max(getChildAt(mSrcPos - firstPos).getTop(), topLimit);\n\t\t\t}\n\t\t}\n\t\t// bottom limit is top of first footer View or\n\t\t// bottom of last item in list\n\t\tint bottomLimit = getHeight() - getPaddingBottom();\n\t\tif (lastPos >= getCount() - numFooters - 1) {\n\t\t\tbottomLimit = getChildAt(getCount() - numFooters - 1 - firstPos).getBottom();\n\t\t}\n\t\tif ((mDragFlags & DRAG_POS_Y) == 0) {\n\t\t\tif (lastPos >= mSrcPos) {\n\t\t\t\tbottomLimit = Math.min(getChildAt(mSrcPos - firstPos).getBottom(), bottomLimit);\n\t\t\t}\n\t\t}\n\n\t\t// Log.d(\"mobeta\", \"dragView top=\" + (y - mDragDeltaY));\n\t\t// Log.d(\"mobeta\", \"limit=\" + limit);\n\t\t// Log.d(\"mobeta\", \"mDragDeltaY=\" + mDragDeltaY);\n\n\t\tif (floatY < topLimit) {\n\t\t\tmFloatLoc.y = topLimit;\n\t\t} else if (floatY + mFloatViewHeight > bottomLimit) {\n\t\t\tmFloatLoc.y = bottomLimit - mFloatViewHeight;\n\t\t}\n\n\t\t// get y-midpoint of floating view (constrained to ListView bounds)\n\t\tmFloatViewMid = mFloatLoc.y + mFloatViewHeightHalf;\n\t}\n\n\tprivate void destroyFloatView() {\n\t\tif (mFloatView != null) {\n\t\t\tmFloatView.setVisibility(GONE);\n\t\t\tif (mFloatViewManager != null) {\n\t\t\t\tmFloatViewManager.onDestroyFloatView(mFloatView);\n\t\t\t}\n\t\t\tmFloatView = null;\n\t\t\tinvalidate();\n\t\t}\n\t}\n\n\t/**\n\t * Interface for customization of the floating View appearance\n\t * and dragging behavior. Implement\n\t * your own and pass it to {@link #setFloatViewManager}. If\n\t * your own is not passed, the default {@link SimpleFloatViewManager}\n\t * implementation is used.\n\t */\n\tpublic interface FloatViewManager {\n\t\t/**\n\t\t * Return the floating View for item at <code>position</code>.\n\t\t * DragSortListView will measure and layout this View for you,\n\t\t * so feel free to just inflate it. You can help DSLV by\n\t\t * setting some {@link ViewGroup.LayoutParams} on this View;\n\t\t * otherwise it will set some for you (with a width of FILL_PARENT\n\t\t * and a height of WRAP_CONTENT).\n\t\t *\n\t\t * @param position Position of item to drag (NOTE:\n\t\t *                 <code>position</code> excludes header Views; thus, if you\n\t\t *                 want to call {@link ListView#getChildAt(int)}, you will need\n\t\t *                 to add {@link ListView#getHeaderViewsCount()} to the index).\n\t\t * @return The View you wish to display as the floating View.\n\t\t */\n\t\tpublic View onCreateFloatView(int position);\n\n\t\t/**\n\t\t * Called whenever the floating View is dragged. Float View\n\t\t * properties can be changed here. Also, the upcoming location\n\t\t * of the float View can be altered by setting\n\t\t * <code>location.x</code> and <code>location.y</code>.\n\t\t *\n\t\t * @param floatView     The floating View.\n\t\t * @param location      The location (top-left; relative to DSLV\n\t\t *                      top-left) at which the float\n\t\t *                      View would like to appear, given the current touch location\n\t\t *                      and the offset provided in {@link DragSortListView#startDrag}.\n\t\t * @param touch         The current touch location (relative to DSLV\n\t\t *                      top-left).\n\t\t * @param pendingScroll\n\t\t */\n\t\tpublic void onDragFloatView(View floatView, Point location, Point touch);\n\n\t\t/**\n\t\t * Called when the float View is dropped; lets you perform\n\t\t * any necessary cleanup. The internal DSLV floating View\n\t\t * reference is set to null immediately after this is called.\n\t\t *\n\t\t * @param floatView The floating View passed to\n\t\t *                  {@link #onCreateFloatView(int)}.\n\t\t */\n\t\tpublic void onDestroyFloatView(View floatView);\n\t}\n\n\tpublic void setFloatViewManager(FloatViewManager manager) {\n\t\tmFloatViewManager = manager;\n\t}\n\n\tpublic void setDragListener(DragListener l) {\n\t\tmDragListener = l;\n\t}\n\n\t/**\n\t * Allows for easy toggling between a DragSortListView\n\t * and a regular old ListView. If enabled, items are\n\t * draggable, where the drag init mode determines how\n\t * items are lifted (see {@link setDragInitMode(int)}).\n\t * If disabled, items cannot be dragged.\n\t *\n\t * @param enabled Set <code>true</code> to enable list\n\t *                item dragging\n\t */\n\tpublic void setDragEnabled(boolean enabled) {\n\t\tmDragEnabled = enabled;\n\t}\n\n\tpublic boolean isDragEnabled() {\n\t\treturn mDragEnabled;\n\t}\n\n\t/**\n\t * This better reorder your ListAdapter! DragSortListView does not do this\n\t * for you; doesn't make sense to. Make sure\n\t * {@link BaseAdapter#notifyDataSetChanged()} or something like it is called\n\t * in your implementation. Furthermore, if you have a choiceMode other than\n\t * none and the ListAdapter does not return true for\n\t * {@link ListAdapter#hasStableIds()}, you will need to call\n\t * {@link #moveCheckState(int, int)} to move the check boxes along with the\n\t * list items.\n\t *\n\t * @param l\n\t */\n\tpublic void setDropListener(DropListener l) {\n\t\tmDropListener = l;\n\t}\n\n\t/**\n\t * Probably a no-brainer, but make sure that your remove listener\n\t * calls {@link BaseAdapter#notifyDataSetChanged()} or something like it.\n\t * When an item removal occurs, DragSortListView\n\t * relies on a redraw of all the items to recover invisible views\n\t * and such. Strictly speaking, if you remove something, your dataset\n\t * has changed...\n\t *\n\t * @param l\n\t */\n\tpublic void setRemoveListener(RemoveListener l) {\n\t\tmRemoveListener = l;\n\t}\n\n\tpublic interface DragListener {\n\t\tpublic void drag(int from, int to);\n\t}\n\n\t/**\n\t * Your implementation of this has to reorder your ListAdapter!\n\t * Make sure to call\n\t * {@link BaseAdapter#notifyDataSetChanged()} or something like it\n\t * in your implementation.\n\t *\n\t * @author heycosmo\n\t */\n\tpublic interface DropListener {\n\t\tpublic void drop(int from, int to);\n\t}\n\n\t/**\n\t * Make sure to call\n\t * {@link BaseAdapter#notifyDataSetChanged()} or something like it\n\t * in your implementation.\n\t *\n\t * @author heycosmo\n\t */\n\tpublic interface RemoveListener {\n\t\tpublic void remove(int which);\n\t}\n\n\tpublic interface DragSortListener extends DropListener, DragListener, RemoveListener {\n\t}\n\n\tpublic void setDragSortListener(DragSortListener l) {\n\t\tsetDropListener(l);\n\t\tsetDragListener(l);\n\t\tsetRemoveListener(l);\n\t}\n\n\t/**\n\t * Completely custom scroll speed profile. Default increases linearly\n\t * with position and is constant in time. Create your own by implementing\n\t * {@link DragSortListView.DragScrollProfile}.\n\t *\n\t * @param ssp\n\t */\n\tpublic void setDragScrollProfile(DragScrollProfile ssp) {\n\t\tif (ssp != null) {\n\t\t\tmScrollProfile = ssp;\n\t\t}\n\t}\n\n\t/**\n\t * Use this to move the check state of an item from one position to another\n\t * in a drop operation. If you have a choiceMode which is not none, this\n\t * method must be called when the order of items changes in an underlying\n\t * adapter which does not have stable IDs (see\n\t * {@link ListAdapter#hasStableIds()}). This is because without IDs, the\n\t * ListView has no way of knowing which items have moved where, and cannot\n\t * update the check state accordingly.\n\t * <p>\n\t * A word of warning about a \"feature\" in Android that you may run into when\n\t * dealing with movable list items: for an adapter that <em>does</em> have\n\t * stable IDs, ListView will attempt to locate each item based on its ID and\n\t * move the check state from the item's old position to the new position —\n\t * which is all fine and good (and removes the need for calling this\n\t * function), except for the half-baked approach. Apparently to save time in\n\t * the naive algorithm used, ListView will only search for an ID in the\n\t * close neighborhood of the old position. If the user moves an item too far\n\t * (specifically, more than 20 rows away), ListView will give up and just\n\t * force the item to be unchecked. So if there is a reasonable chance that\n\t * the user will move items more than 20 rows away from the original\n\t * position, you may wish to use an adapter with unstable IDs and call this\n\t * method manually instead.\n\t *\n\t * @param from\n\t * @param to\n\t */\n\tpublic void moveCheckState(int from, int to) {\n\t\t// This method runs in O(n log n) time (n being the number of list\n\t\t// items). The bottleneck is the call to AbsListView.setItemChecked,\n\t\t// which is O(log n) because of the binary search involved in calling\n\t\t// SparseBooleanArray.put().\n\t\t//\n\t\t// To improve on the average time, we minimize the number of calls to\n\t\t// setItemChecked by only calling it for items that actually have a\n\t\t// changed state. This is achieved by building a list containing the\n\t\t// start and end of the \"runs\" of checked items, and then moving the\n\t\t// runs. Note that moving an item from A to B is essentially a rotation\n\t\t// of the range of items in [A, B]. Let's say we have\n\t\t// . . U V X Y Z . .\n\t\t// and move U after Z. This is equivalent to a rotation one step to the\n\t\t// left within the range you are moving across:\n\t\t// . . V X Y Z U . .\n\t\t//\n\t\t// So, to perform the move we enumerate all the runs within the move\n\t\t// range, then rotate each run one step to the left or right (depending\n\t\t// on move direction). For example, in the list:\n\t\t// X X . X X X . X\n\t\t// we have two runs. One begins at the last item of the list and wraps\n\t\t// around to the beginning, ending at position 1. The second begins at\n\t\t// position 3 and ends at position 5. To rotate a run, regardless of\n\t\t// length, we only need to set a check mark at one end of the run, and\n\t\t// clear a check mark at the other end:\n\t\t// X . X X X . X X\n\t\tSparseBooleanArray cip = getCheckedItemPositions();\n\t\tint rangeStart = from;\n\t\tint rangeEnd = to;\n\t\tif (to < from) {\n\t\t\trangeStart = to;\n\t\t\trangeEnd = from;\n\t\t}\n\t\trangeEnd += 1;\n\n\t\tint[] runStart = new int[cip.size()];\n\t\tint[] runEnd = new int[cip.size()];\n\t\tint runCount = buildRunList(cip, rangeStart, rangeEnd, runStart, runEnd);\n\t\tif (runCount == 1 && (runStart[0] == runEnd[0])) {\n\t\t\t// Special case where all items are checked, we can never set any\n\t\t\t// item to false like we do below.\n\t\t\treturn;\n\t\t}\n\n\t\tif (from < to) {\n\t\t\tfor (int i = 0; i != runCount; i++) {\n\t\t\t\tsetItemChecked(rotate(runStart[i], -1, rangeStart, rangeEnd), true);\n\t\t\t\tsetItemChecked(rotate(runEnd[i], -1, rangeStart, rangeEnd), false);\n\t\t\t}\n\n\t\t} else {\n\t\t\tfor (int i = 0; i != runCount; i++) {\n\t\t\t\tsetItemChecked(runStart[i], false);\n\t\t\t\tsetItemChecked(runEnd[i], true);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Use this when an item has been deleted, to move the check state of all\n\t * following items up one step. If you have a choiceMode which is not none,\n\t * this method must be called when the order of items changes in an\n\t * underlying adapter which does not have stable IDs (see\n\t * {@link ListAdapter#hasStableIds()}). This is because without IDs, the\n\t * ListView has no way of knowing which items have moved where, and cannot\n\t * update the check state accordingly.\n\t *\n\t * See also further comments on {@link #moveCheckState(int, int)}.\n\t *\n\t * @param position\n\t */\n\tpublic void removeCheckState(int position) {\n\t\tSparseBooleanArray cip = getCheckedItemPositions();\n\n\t\tif (cip.size() == 0)\n\t\t\treturn;\n\t\tint[] runStart = new int[cip.size()];\n\t\tint[] runEnd = new int[cip.size()];\n\t\tint rangeStart = position;\n\t\tint rangeEnd = cip.keyAt(cip.size() - 1) + 1;\n\t\tint runCount = buildRunList(cip, rangeStart, rangeEnd, runStart, runEnd);\n\t\tfor (int i = 0; i != runCount; i++) {\n\t\t\tif (!(runStart[i] == position || (runEnd[i] < runStart[i] && runEnd[i] > position))) {\n\t\t\t\t// Only set a new check mark in front of this run if it does\n\t\t\t\t// not contain the deleted position. If it does, we only need\n\t\t\t\t// to make it one check mark shorter at the end.\n\t\t\t\tsetItemChecked(rotate(runStart[i], -1, rangeStart, rangeEnd), true);\n\t\t\t}\n\t\t\tsetItemChecked(rotate(runEnd[i], -1, rangeStart, rangeEnd), false);\n\t\t}\n\t}\n\n\tprivate static int buildRunList(SparseBooleanArray cip, int rangeStart,\n\t\t\t\t\t\t\t\t\tint rangeEnd, int[] runStart, int[] runEnd) {\n\t\tint runCount = 0;\n\n\t\tint i = findFirstSetIndex(cip, rangeStart, rangeEnd);\n\t\tif (i == -1)\n\t\t\treturn 0;\n\n\t\tint position = cip.keyAt(i);\n\t\tint currentRunStart = position;\n\t\tint currentRunEnd = currentRunStart + 1;\n\t\tfor (i++; i < cip.size() && (position = cip.keyAt(i)) < rangeEnd; i++) {\n\t\t\tif (!cip.valueAt(i)) // not checked => not interesting\n\t\t\t\tcontinue;\n\t\t\tif (position == currentRunEnd) {\n\t\t\t\tcurrentRunEnd++;\n\t\t\t} else {\n\t\t\t\trunStart[runCount] = currentRunStart;\n\t\t\t\trunEnd[runCount] = currentRunEnd;\n\t\t\t\trunCount++;\n\t\t\t\tcurrentRunStart = position;\n\t\t\t\tcurrentRunEnd = position + 1;\n\t\t\t}\n\t\t}\n\n\t\tif (currentRunEnd == rangeEnd) {\n\t\t\t// rangeStart and rangeEnd are equivalent positions so to be\n\t\t\t// consistent we translate them to the same integer value. That way\n\t\t\t// we can check whether a run covers the entire range by just\n\t\t\t// checking if the start equals the end position.\n\t\t\tcurrentRunEnd = rangeStart;\n\t\t}\n\t\trunStart[runCount] = currentRunStart;\n\t\trunEnd[runCount] = currentRunEnd;\n\t\trunCount++;\n\n\t\tif (runCount > 1) {\n\t\t\tif (runStart[0] == rangeStart && runEnd[runCount - 1] == rangeStart) {\n\t\t\t\t// The last run ends at the end of the range, and the first run\n\t\t\t\t// starts at the beginning of the range. So they are actually\n\t\t\t\t// part of the same run, except they wrap around the end of the\n\t\t\t\t// range. To avoid adjacent runs, we need to merge them.\n\t\t\t\trunStart[0] = runStart[runCount - 1];\n\t\t\t\trunCount--;\n\t\t\t}\n\t\t}\n\t\treturn runCount;\n\t}\n\n\tprivate static int rotate(int value, int offset, int lowerBound, int upperBound) {\n\t\tint windowSize = upperBound - lowerBound;\n\n\t\tvalue += offset;\n\t\tif (value < lowerBound) {\n\t\t\tvalue += windowSize;\n\t\t} else if (value >= upperBound) {\n\t\t\tvalue -= windowSize;\n\t\t}\n\t\treturn value;\n\t}\n\n\tprivate static int findFirstSetIndex(SparseBooleanArray sba, int rangeStart, int rangeEnd) {\n\t\tint size = sba.size();\n\t\tint i = insertionIndexForKey(sba, rangeStart);\n\t\twhile (i < size && sba.keyAt(i) < rangeEnd && !sba.valueAt(i))\n\t\t\ti++;\n\t\tif (i == size || sba.keyAt(i) >= rangeEnd)\n\t\t\treturn -1;\n\t\treturn i;\n\t}\n\n\tprivate static int insertionIndexForKey(SparseBooleanArray sba, int key) {\n\t\tint low = 0;\n\t\tint high = sba.size();\n\t\twhile (high - low > 0) {\n\t\t\tint middle = (low + high) >> 1;\n\t\t\tif (sba.keyAt(middle) < key)\n\t\t\t\tlow = middle + 1;\n\t\t\telse\n\t\t\t\thigh = middle;\n\t\t}\n\t\treturn low;\n\t}\n\n\t/**\n\t * Interface for controlling\n\t * scroll speed as a function of touch position and time. Use\n\t * {@link DragSortListView#setDragScrollProfile(DragScrollProfile)} to\n\t * set custom profile.\n\t *\n\t * @author heycosmo\n\t */\n\tpublic interface DragScrollProfile {\n\t\t/**\n\t\t * Return a scroll speed in pixels/millisecond. Always return a\n\t\t * positive number.\n\t\t *\n\t\t * @param w Normalized position in scroll region (i.e. w \\in [0,1]).\n\t\t *          Small w typically means slow scrolling.\n\t\t * @param t Time (in milliseconds) since start of scroll (handy if you\n\t\t *          want scroll acceleration).\n\t\t * @return Scroll speed at position w and time t in pixels/ms.\n\t\t */\n\t\tfloat getSpeed(float w, long t);\n\t}\n\n\tprivate class DragScroller implements Runnable {\n\n\t\tprivate boolean mAbort;\n\n\t\tprivate long mPrevTime;\n\t\tprivate long mCurrTime;\n\n\t\tprivate int dy;\n\t\tprivate float dt;\n\t\tprivate long tStart;\n\t\tprivate int scrollDir;\n\n\t\tpublic final static int STOP = -1;\n\t\tpublic final static int UP = 0;\n\t\tpublic final static int DOWN = 1;\n\n\t\tprivate float mScrollSpeed; // pixels per ms\n\n\t\tprivate boolean mScrolling = false;\n\n\t\tprivate int mLastHeader;\n\t\tprivate int mFirstFooter;\n\n\t\tpublic boolean isScrolling() {\n\t\t\treturn mScrolling;\n\t\t}\n\n\t\tpublic int getScrollDir() {\n\t\t\treturn mScrolling ? scrollDir : STOP;\n\t\t}\n\n\t\tpublic DragScroller() {\n\t\t}\n\n\t\tpublic void startScrolling(int dir) {\n\t\t\tif (!mScrolling) {\n\t\t\t\t// Debug.startMethodTracing(\"dslv-scroll\");\n\t\t\t\tmAbort = false;\n\t\t\t\tmScrolling = true;\n\t\t\t\ttStart = SystemClock.uptimeMillis();\n\t\t\t\tmPrevTime = tStart;\n\t\t\t\tscrollDir = dir;\n\t\t\t\tpost(this);\n\t\t\t}\n\t\t}\n\n\t\tpublic void stopScrolling(boolean now) {\n\t\t\tif (now) {\n\t\t\t\tDragSortListView.this.removeCallbacks(this);\n\t\t\t\tmScrolling = false;\n\t\t\t} else {\n\t\t\t\tmAbort = true;\n\t\t\t}\n\n\t\t\t// Debug.stopMethodTracing();\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (mAbort) {\n\t\t\t\tmScrolling = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Log.d(\"mobeta\", \"scroll\");\n\n\t\t\tfinal int first = getFirstVisiblePosition();\n\t\t\tfinal int last = getLastVisiblePosition();\n\t\t\tfinal int count = getCount();\n\t\t\tfinal int padTop = getPaddingTop();\n\t\t\tfinal int listHeight = getHeight() - padTop - getPaddingBottom();\n\n\t\t\tint minY = Math.min(mY, mFloatViewMid + mFloatViewHeightHalf);\n\t\t\tint maxY = Math.max(mY, mFloatViewMid - mFloatViewHeightHalf);\n\n\t\t\tif (scrollDir == UP) {\n\t\t\t\tView v = getChildAt(0);\n\t\t\t\t// Log.d(\"mobeta\", \"vtop=\"+v.getTop()+\" padtop=\"+padTop);\n\t\t\t\tif (v == null) {\n\t\t\t\t\tmScrolling = false;\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n\t\t\t\t\tif (first == 0 && v.getTop() == padTop) {\n\t\t\t\t\t\tmScrolling = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmScrollSpeed = mScrollProfile.getSpeed((mUpScrollStartYF - maxY)\n\t\t\t\t\t\t/ mDragUpScrollHeight, mPrevTime);\n\t\t\t} else {\n\t\t\t\tView v = getChildAt(last - first);\n\t\t\t\tif (v == null) {\n\t\t\t\t\tmScrolling = false;\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n\t\t\t\t\tif (last == count - 1 && v.getBottom() <= listHeight + padTop) {\n\t\t\t\t\t\tmScrolling = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmScrollSpeed = -mScrollProfile.getSpeed((minY - mDownScrollStartYF)\n\t\t\t\t\t\t/ mDragDownScrollHeight, mPrevTime);\n\t\t\t}\n\n\t\t\tmCurrTime = SystemClock.uptimeMillis();\n\t\t\tdt = (float) (mCurrTime - mPrevTime);\n\n\t\t\t// dy is change in View position of a list item; i.e. positive dy\n\t\t\t// means user is scrolling up (list item moves down the screen,\n\t\t\t// remember\n\t\t\t// y=0 is at top of View).\n\t\t\tdy = (int) Math.round(mScrollSpeed * dt);\n\n\t\t\tint movePos;\n\t\t\tif (dy >= 0) {\n\t\t\t\tdy = Math.min(listHeight, dy);\n\t\t\t\tmovePos = first;\n\t\t\t} else {\n\t\t\t\tdy = Math.max(-listHeight, dy);\n\t\t\t\tmovePos = last;\n\t\t\t}\n\n\t\t\tfinal View moveItem = getChildAt(movePos - first);\n\t\t\tint top = moveItem.getTop() + dy;\n\n\t\t\tif (movePos == 0 && top > padTop) {\n\t\t\t\ttop = padTop;\n\t\t\t}\n\n\t\t\t// always do scroll\n\t\t\tmBlockLayoutRequests = true;\n\n\t\t\tsetSelectionFromTop(movePos, top - padTop);\n\t\t\tDragSortListView.this.layoutChildren();\n\t\t\tinvalidate();\n\n\t\t\tmBlockLayoutRequests = false;\n\n\t\t\t// scroll means relative float View movement\n\t\t\tdoDragFloatView(movePos, moveItem, false);\n\n\t\t\tmPrevTime = mCurrTime;\n\t\t\t// Log.d(\"mobeta\", \"  updated prevTime=\"+mPrevTime);\n\n\t\t\tpost(this);\n\t\t}\n\t}\n\n\tprivate class DragSortTracker {\n\t\tStringBuilder mBuilder = new StringBuilder();\n\n\t\tFile mFile;\n\n\t\tprivate int mNumInBuffer = 0;\n\t\tprivate int mNumFlushes = 0;\n\n\t\tprivate boolean mTracking = false;\n\n\t\tpublic DragSortTracker() {\n\t\t\tFile root = Environment.getExternalStorageDirectory();\n\t\t\tmFile = new File(root, \"dslv_state.txt\");\n\n\t\t\tif (!mFile.exists()) {\n\t\t\t\ttry {\n\t\t\t\t\tmFile.createNewFile();\n\t\t\t\t\tLog.d(\"mobeta\", \"file created\");\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLog.w(\"mobeta\", \"Could not create dslv_state.txt\");\n\t\t\t\t\tLog.d(\"mobeta\", e.getMessage());\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tpublic void startTracking() {\n\t\t\tmBuilder.append(\"<DSLVStates>\\n\");\n\t\t\tmNumFlushes = 0;\n\t\t\tmTracking = true;\n\t\t}\n\n\t\tpublic void appendState() {\n\t\t\tif (!mTracking) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmBuilder.append(\"<DSLVState>\\n\");\n\t\t\tfinal int children = getChildCount();\n\t\t\tfinal int first = getFirstVisiblePosition();\n\t\t\tmBuilder.append(\"    <Positions>\");\n\t\t\tfor (int i = 0; i < children; ++i) {\n\t\t\t\tmBuilder.append(first + i).append(\",\");\n\t\t\t}\n\t\t\tmBuilder.append(\"</Positions>\\n\");\n\n\t\t\tmBuilder.append(\"    <Tops>\");\n\t\t\tfor (int i = 0; i < children; ++i) {\n\t\t\t\tmBuilder.append(getChildAt(i).getTop()).append(\",\");\n\t\t\t}\n\t\t\tmBuilder.append(\"</Tops>\\n\");\n\t\t\tmBuilder.append(\"    <Bottoms>\");\n\t\t\tfor (int i = 0; i < children; ++i) {\n\t\t\t\tmBuilder.append(getChildAt(i).getBottom()).append(\",\");\n\t\t\t}\n\t\t\tmBuilder.append(\"</Bottoms>\\n\");\n\n\t\t\tmBuilder.append(\"    <FirstExpPos>\").append(mFirstExpPos).append(\"</FirstExpPos>\\n\");\n\t\t\tmBuilder.append(\"    <FirstExpBlankHeight>\")\n\t\t\t\t\t.append(getItemHeight(mFirstExpPos) - getChildHeight(mFirstExpPos))\n\t\t\t\t\t.append(\"</FirstExpBlankHeight>\\n\");\n\t\t\tmBuilder.append(\"    <SecondExpPos>\").append(mSecondExpPos).append(\"</SecondExpPos>\\n\");\n\t\t\tmBuilder.append(\"    <SecondExpBlankHeight>\")\n\t\t\t\t\t.append(getItemHeight(mSecondExpPos) - getChildHeight(mSecondExpPos))\n\t\t\t\t\t.append(\"</SecondExpBlankHeight>\\n\");\n\t\t\tmBuilder.append(\"    <SrcPos>\").append(mSrcPos).append(\"</SrcPos>\\n\");\n\t\t\tmBuilder.append(\"    <SrcHeight>\").append(mFloatViewHeight + getDividerHeight())\n\t\t\t\t\t.append(\"</SrcHeight>\\n\");\n\t\t\tmBuilder.append(\"    <ViewHeight>\").append(getHeight()).append(\"</ViewHeight>\\n\");\n\t\t\tmBuilder.append(\"    <LastY>\").append(mLastY).append(\"</LastY>\\n\");\n\t\t\tmBuilder.append(\"    <FloatY>\").append(mFloatViewMid).append(\"</FloatY>\\n\");\n\t\t\tmBuilder.append(\"    <ShuffleEdges>\");\n\t\t\tfor (int i = 0; i < children; ++i) {\n\t\t\t\tmBuilder.append(getShuffleEdge(first + i, getChildAt(i).getTop())).append(\",\");\n\t\t\t}\n\t\t\tmBuilder.append(\"</ShuffleEdges>\\n\");\n\n\t\t\tmBuilder.append(\"</DSLVState>\\n\");\n\t\t\tmNumInBuffer++;\n\n\t\t\tif (mNumInBuffer > 1000) {\n\t\t\t\tflush();\n\t\t\t\tmNumInBuffer = 0;\n\t\t\t}\n\t\t}\n\n\t\tpublic void flush() {\n\t\t\tif (!mTracking) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// save to file on sdcard\n\t\t\ttry {\n\t\t\t\tboolean append = true;\n\t\t\t\tif (mNumFlushes == 0) {\n\t\t\t\t\tappend = false;\n\t\t\t\t}\n\t\t\t\tFileWriter writer = new FileWriter(mFile, append);\n\n\t\t\t\twriter.write(mBuilder.toString());\n\t\t\t\tmBuilder.delete(0, mBuilder.length());\n\n\t\t\t\twriter.flush();\n\t\t\t\twriter.close();\n\n\t\t\t\tmNumFlushes++;\n\t\t\t} catch (IOException e) {\n\t\t\t\t// do nothing\n\t\t\t}\n\t\t}\n\n\t\tpublic void stopTracking() {\n\t\t\tif (mTracking) {\n\t\t\t\tmBuilder.append(\"</DSLVStates>\\n\");\n\t\t\t\tflush();\n\t\t\t\tmTracking = false;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/ResourceDragSortCursorAdapter.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.mobeta.android.dslv;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.LayoutInflater;\n\n// taken from v4 rev. 10 ResourceCursorAdapter.java\n\n/**\n * Static library support version of the framework's {@link android.widget.ResourceCursorAdapter}.\n * Used to write apps that run on platforms prior to Android 3.0.  When running\n * on Android 3.0 or above, this implementation is still used; it does not try\n * to switch to the framework's implementation.  See the framework SDK\n * documentation for a class overview.\n */\npublic abstract class ResourceDragSortCursorAdapter extends DragSortCursorAdapter {\n\tprivate int mLayout;\n\n\tprivate int mDropDownLayout;\n\n\tprivate LayoutInflater mInflater;\n\n\t/**\n\t * Constructor the enables auto-requery.\n\t *\n\t * @param context The context where the ListView associated with this adapter is running\n\t * @param layout  resource identifier of a layout file that defines the views\n\t *                for this list item.  Unless you override them later, this will\n\t *                define both the item views and the drop down views.\n\t * @deprecated This option is discouraged, as it results in Cursor queries\n\t * being performed on the application's UI thread and thus can cause poor\n\t * responsiveness or even Application Not Responding errors.  As an alternative,\n\t * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.\n\t */\n\t@Deprecated\n\tpublic ResourceDragSortCursorAdapter(Context context, int layout, Cursor c) {\n\t\tsuper(context, c);\n\t\tmLayout = mDropDownLayout = layout;\n\t\tmInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n\t}\n\n\t/**\n\t * Constructor with default behavior as per\n\t * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended\n\t * you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.\n\t * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}\n\t * will always be set.\n\t *\n\t * @param context     The context where the ListView associated with this adapter is running\n\t * @param layout      resource identifier of a layout file that defines the views\n\t *                    for this list item.  Unless you override them later, this will\n\t *                    define both the item views and the drop down views.\n\t * @param c           The cursor from which to get the data.\n\t * @param autoRequery If true the adapter will call requery() on the\n\t *                    cursor whenever it changes so the most recent\n\t *                    data is always displayed.  Using true here is discouraged.\n\t */\n\tpublic ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) {\n\t\tsuper(context, c, autoRequery);\n\t\tmLayout = mDropDownLayout = layout;\n\t\tmInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n\t}\n\n\t/**\n\t * Standard constructor.\n\t *\n\t * @param context The context where the ListView associated with this adapter is running\n\t * @param layout  Resource identifier of a layout file that defines the views\n\t *                for this list item.  Unless you override them later, this will\n\t *                define both the item views and the drop down views.\n\t * @param c       The cursor from which to get the data.\n\t * @param flags   Flags used to determine the behavior of the adapter,\n\t *                as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.\n\t */\n\tpublic ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, int flags) {\n\t\tsuper(context, c, flags);\n\t\tmLayout = mDropDownLayout = layout;\n\t\tmInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n\t}\n\n\t/**\n\t * Inflates view(s) from the specified XML file.\n\t *\n\t * @see android.widget.CursorAdapter#newView(android.content.Context,\n\t * android.database.Cursor, ViewGroup)\n\t */\n\t@Override\n\tpublic View newView(Context context, Cursor cursor, ViewGroup parent) {\n\t\treturn mInflater.inflate(mLayout, parent, false);\n\t}\n\n\t@Override\n\tpublic View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {\n\t\treturn mInflater.inflate(mDropDownLayout, parent, false);\n\t}\n\n\t/**\n\t * <p>Sets the layout resource of the item views.</p>\n\t *\n\t * @param layout the layout resources used to create item views\n\t */\n\tpublic void setViewResource(int layout) {\n\t\tmLayout = layout;\n\t}\n\n\t/**\n\t * <p>Sets the layout resource of the drop down views.</p>\n\t *\n\t * @param dropDownLayout the layout resources used to create drop down views\n\t */\n\tpublic void setDropDownViewResource(int dropDownLayout) {\n\t\tmDropDownLayout = dropDownLayout;\n\t}\n}\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/SimpleDragSortCursorAdapter.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.mobeta.android.dslv;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.view.View;\nimport android.widget.TextView;\nimport android.widget.ImageView;\n\n// taken from sdk/sources/android-16/android/widget/SimpleCursorAdapter.java\n\n/**\n * An easy adapter to map columns from a cursor to TextViews or ImageViews\n * defined in an XML file. You can specify which columns you want, which\n * views you want to display the columns, and the XML file that defines\n * the appearance of these views.\n *\n * Binding occurs in two phases. First, if a\n * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,\n * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}\n * is invoked. If the returned value is true, binding has occured. If the\n * returned value is false and the view to bind is a TextView,\n * {@link #setViewText(TextView, String)} is invoked. If the returned value\n * is false and the view to bind is an ImageView,\n * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate\n * binding can be found, an {@link IllegalStateException} is thrown.\n *\n * If this adapter is used with filtering, for instance in an\n * {@link android.widget.AutoCompleteTextView}, you can use the\n * {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} and the\n * {@link android.widget.FilterQueryProvider} interfaces\n * to get control over the filtering process. You can refer to\n * {@link #convertToString(android.database.Cursor)} and\n * {@link #runQueryOnBackgroundThread(CharSequence)} for more information.\n */\npublic class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter {\n\t/**\n\t * A list of columns containing the data to bind to the UI.\n\t * This field should be made private, so it is hidden from the SDK.\n\t * {@hide}\n\t */\n\tprotected int[] mFrom;\n\t/**\n\t * A list of View ids representing the views to which the data must be bound.\n\t * This field should be made private, so it is hidden from the SDK.\n\t * {@hide}\n\t */\n\tprotected int[] mTo;\n\n\tprivate int mStringConversionColumn = -1;\n\tprivate CursorToStringConverter mCursorToStringConverter;\n\tprivate ViewBinder mViewBinder;\n\n\tString[] mOriginalFrom;\n\n\t/**\n\t * Constructor the enables auto-requery.\n\t *\n\t * @deprecated This option is discouraged, as it results in Cursor queries\n\t * being performed on the application's UI thread and thus can cause poor\n\t * responsiveness or even Application Not Responding errors.  As an alternative,\n\t * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.\n\t */\n\t@Deprecated\n\tpublic SimpleDragSortCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {\n\t\tsuper(context, layout, c);\n\t\tmTo = to;\n\t\tmOriginalFrom = from;\n\t\tfindColumns(c, from);\n\t}\n\n\t/**\n\t * Standard constructor.\n\t *\n\t * @param context The context where the ListView associated with this\n\t *                SimpleListItemFactory is running\n\t * @param layout  resource identifier of a layout file that defines the views\n\t *                for this list item. The layout file should include at least\n\t *                those named views defined in \"to\"\n\t * @param c       The database cursor.  Can be null if the cursor is not available yet.\n\t * @param from    A list of column names representing the data to bind to the UI.  Can be null\n\t *                if the cursor is not available yet.\n\t * @param to      The views that should display column in the \"from\" parameter.\n\t *                These should all be TextViews. The first N views in this list\n\t *                are given the values of the first N columns in the from\n\t *                parameter.  Can be null if the cursor is not available yet.\n\t * @param flags   Flags used to determine the behavior of the adapter,\n\t *                as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.\n\t */\n\tpublic SimpleDragSortCursorAdapter(Context context, int layout,\n\t\t\t\t\t\t\t\t\t   Cursor c, String[] from, int[] to, int flags) {\n\t\tsuper(context, layout, c, flags);\n\t\tmTo = to;\n\t\tmOriginalFrom = from;\n\t\tfindColumns(c, from);\n\t}\n\n\t/**\n\t * Binds all of the field names passed into the \"to\" parameter of the\n\t * constructor with their corresponding cursor columns as specified in the\n\t * \"from\" parameter.\n\t *\n\t * Binding occurs in two phases. First, if a\n\t * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,\n\t * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}\n\t * is invoked. If the returned value is true, binding has occured. If the\n\t * returned value is false and the view to bind is a TextView,\n\t * {@link #setViewText(TextView, String)} is invoked. If the returned value is\n\t * false and the view to bind is an ImageView,\n\t * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate\n\t * binding can be found, an {@link IllegalStateException} is thrown.\n\t *\n\t * @throws IllegalStateException if binding cannot occur\n\t * @see android.widget.CursorAdapter#bindView(android.view.View,\n\t * android.content.Context, android.database.Cursor)\n\t * @see #getViewBinder()\n\t * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)\n\t * @see #setViewImage(ImageView, String)\n\t * @see #setViewText(TextView, String)\n\t */\n\t@Override\n\tpublic void bindView(View view, Context context, Cursor cursor) {\n\t\tfinal ViewBinder binder = mViewBinder;\n\t\tfinal int count = mTo.length;\n\t\tfinal int[] from = mFrom;\n\t\tfinal int[] to = mTo;\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tfinal View v = view.findViewById(to[i]);\n\t\t\tif (v != null) {\n\t\t\t\tboolean bound = false;\n\t\t\t\tif (binder != null) {\n\t\t\t\t\tbound = binder.setViewValue(v, cursor, from[i]);\n\t\t\t\t}\n\n\t\t\t\tif (!bound) {\n\t\t\t\t\tString text = cursor.getString(from[i]);\n\t\t\t\t\tif (text == null) {\n\t\t\t\t\t\ttext = \"\";\n\t\t\t\t\t}\n\n\t\t\t\t\tif (v instanceof TextView) {\n\t\t\t\t\t\tsetViewText((TextView) v, text);\n\t\t\t\t\t} else if (v instanceof ImageView) {\n\t\t\t\t\t\tsetViewImage((ImageView) v, text);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new IllegalStateException(v.getClass().getName() + \" is not a \" +\n\t\t\t\t\t\t\t\t\" view that can be bounds by this SimpleCursorAdapter\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns the {@link ViewBinder} used to bind data to views.\n\t *\n\t * @return a ViewBinder or null if the binder does not exist\n\t * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)\n\t * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)\n\t */\n\tpublic ViewBinder getViewBinder() {\n\t\treturn mViewBinder;\n\t}\n\n\t/**\n\t * Sets the binder used to bind data to views.\n\t *\n\t * @param viewBinder the binder used to bind data to views, can be null to\n\t *                   remove the existing binder\n\t * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)\n\t * @see #getViewBinder()\n\t */\n\tpublic void setViewBinder(ViewBinder viewBinder) {\n\t\tmViewBinder = viewBinder;\n\t}\n\n\t/**\n\t * Called by bindView() to set the image for an ImageView but only if\n\t * there is no existing ViewBinder or if the existing ViewBinder cannot\n\t * handle binding to an ImageView.\n\t *\n\t * By default, the value will be treated as an image resource. If the\n\t * value cannot be used as an image resource, the value is used as an\n\t * image Uri.\n\t *\n\t * Intended to be overridden by Adapters that need to filter strings\n\t * retrieved from the database.\n\t *\n\t * @param v     ImageView to receive an image\n\t * @param value the value retrieved from the cursor\n\t */\n\tpublic void setViewImage(ImageView v, String value) {\n\t\ttry {\n\t\t\tv.setImageResource(Integer.parseInt(value));\n\t\t} catch (NumberFormatException nfe) {\n\t\t\tv.setImageURI(Uri.parse(value));\n\t\t}\n\t}\n\n\t/**\n\t * Called by bindView() to set the text for a TextView but only if\n\t * there is no existing ViewBinder or if the existing ViewBinder cannot\n\t * handle binding to a TextView.\n\t *\n\t * Intended to be overridden by Adapters that need to filter strings\n\t * retrieved from the database.\n\t *\n\t * @param v    TextView to receive text\n\t * @param text the text to be set for the TextView\n\t */\n\tpublic void setViewText(TextView v, String text) {\n\t\tv.setText(text);\n\t}\n\n\t/**\n\t * Return the index of the column used to get a String representation\n\t * of the Cursor.\n\t *\n\t * @return a valid index in the current Cursor or -1\n\t * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)\n\t * @see #setStringConversionColumn(int)\n\t * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)\n\t * @see #getCursorToStringConverter()\n\t */\n\tpublic int getStringConversionColumn() {\n\t\treturn mStringConversionColumn;\n\t}\n\n\t/**\n\t * Defines the index of the column in the Cursor used to get a String\n\t * representation of that Cursor. The column is used to convert the\n\t * Cursor to a String only when the current CursorToStringConverter\n\t * is null.\n\t *\n\t * @param stringConversionColumn a valid index in the current Cursor or -1 to use the default\n\t *                               conversion mechanism\n\t * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)\n\t * @see #getStringConversionColumn()\n\t * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)\n\t * @see #getCursorToStringConverter()\n\t */\n\tpublic void setStringConversionColumn(int stringConversionColumn) {\n\t\tmStringConversionColumn = stringConversionColumn;\n\t}\n\n\t/**\n\t * Returns the converter used to convert the filtering Cursor\n\t * into a String.\n\t *\n\t * @return null if the converter does not exist or an instance of\n\t * {@link android.widget.SimpleCursorAdapter.CursorToStringConverter}\n\t * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)\n\t * @see #getStringConversionColumn()\n\t * @see #setStringConversionColumn(int)\n\t * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)\n\t */\n\tpublic CursorToStringConverter getCursorToStringConverter() {\n\t\treturn mCursorToStringConverter;\n\t}\n\n\t/**\n\t * Sets the converter  used to convert the filtering Cursor\n\t * into a String.\n\t *\n\t * @param cursorToStringConverter the Cursor to String converter, or\n\t *                                null to remove the converter\n\t * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)\n\t * @see #getStringConversionColumn()\n\t * @see #setStringConversionColumn(int)\n\t * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)\n\t */\n\tpublic void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) {\n\t\tmCursorToStringConverter = cursorToStringConverter;\n\t}\n\n\t/**\n\t * Returns a CharSequence representation of the specified Cursor as defined\n\t * by the current CursorToStringConverter. If no CursorToStringConverter\n\t * has been set, the String conversion column is used instead. If the\n\t * conversion column is -1, the returned String is empty if the cursor\n\t * is null or Cursor.toString().\n\t *\n\t * @param cursor the Cursor to convert to a CharSequence\n\t * @return a non-null CharSequence representing the cursor\n\t */\n\t@Override\n\tpublic CharSequence convertToString(Cursor cursor) {\n\t\tif (mCursorToStringConverter != null) {\n\t\t\treturn mCursorToStringConverter.convertToString(cursor);\n\t\t} else if (mStringConversionColumn > -1) {\n\t\t\treturn cursor.getString(mStringConversionColumn);\n\t\t}\n\n\t\treturn super.convertToString(cursor);\n\t}\n\n\t/**\n\t * Create a map from an array of strings to an array of column-id integers in cursor c.\n\t * If c is null, the array will be discarded.\n\t *\n\t * @param c    the cursor to find the columns from\n\t * @param from the Strings naming the columns of interest\n\t */\n\tprivate void findColumns(Cursor c, String[] from) {\n\t\tif (c != null) {\n\t\t\tint i;\n\t\t\tint count = from.length;\n\t\t\tif (mFrom == null || mFrom.length != count) {\n\t\t\t\tmFrom = new int[count];\n\t\t\t}\n\t\t\tfor (i = 0; i < count; i++) {\n\t\t\t\tmFrom[i] = c.getColumnIndexOrThrow(from[i]);\n\t\t\t}\n\t\t} else {\n\t\t\tmFrom = null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Cursor swapCursor(Cursor c) {\n\t\t// super.swapCursor() will notify observers before we have\n\t\t// a valid mapping, make sure we have a mapping before this\n\t\t// happens\n\t\tfindColumns(c, mOriginalFrom);\n\t\treturn super.swapCursor(c);\n\t}\n\n\t/**\n\t * Change the cursor and change the column-to-view mappings at the same time.\n\t *\n\t * @param c    The database cursor.  Can be null if the cursor is not available yet.\n\t * @param from A list of column names representing the data to bind to the UI.  Can be null\n\t *             if the cursor is not available yet.\n\t * @param to   The views that should display column in the \"from\" parameter.\n\t *             These should all be TextViews. The first N views in this list\n\t *             are given the values of the first N columns in the from\n\t *             parameter.  Can be null if the cursor is not available yet.\n\t */\n\tpublic void changeCursorAndColumns(Cursor c, String[] from, int[] to) {\n\t\tmOriginalFrom = from;\n\t\tmTo = to;\n\t\t// super.changeCursor() will notify observers before we have\n\t\t// a valid mapping, make sure we have a mapping before this\n\t\t// happens\n\t\tfindColumns(c, mOriginalFrom);\n\t\tsuper.changeCursor(c);\n\t}\n\n\t/**\n\t * This class can be used by external clients of SimpleCursorAdapter\n\t * to bind values fom the Cursor to views.\n\t *\n\t * You should use this class to bind values from the Cursor to views\n\t * that are not directly supported by SimpleCursorAdapter or to\n\t * change the way binding occurs for views supported by\n\t * SimpleCursorAdapter.\n\t *\n\t * @see SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)\n\t * @see SimpleCursorAdapter#setViewImage(ImageView, String)\n\t * @see SimpleCursorAdapter#setViewText(TextView, String)\n\t */\n\tpublic static interface ViewBinder {\n\t\t/**\n\t\t * Binds the Cursor column defined by the specified index to the specified view.\n\t\t *\n\t\t * When binding is handled by this ViewBinder, this method must return true.\n\t\t * If this method returns false, SimpleCursorAdapter will attempts to handle\n\t\t * the binding on its own.\n\t\t *\n\t\t * @param view        the view to bind the data to\n\t\t * @param cursor      the cursor to get the data from\n\t\t * @param columnIndex the column at which the data can be found in the cursor\n\t\t * @return true if the data was bound to the view, false otherwise\n\t\t */\n\t\tboolean setViewValue(View view, Cursor cursor, int columnIndex);\n\t}\n\n\t/**\n\t * This class can be used by external clients of SimpleCursorAdapter\n\t * to define how the Cursor should be converted to a String.\n\t *\n\t * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)\n\t */\n\tpublic static interface CursorToStringConverter {\n\t\t/**\n\t\t * Returns a CharSequence representing the specified Cursor.\n\t\t *\n\t\t * @param cursor the cursor for which a CharSequence representation\n\t\t *               is requested\n\t\t * @return a non-null CharSequence representing the cursor\n\t\t */\n\t\tCharSequence convertToString(Cursor cursor);\n\t}\n\n}\n"
  },
  {
    "path": "external/drag-sort-listview/src/com/mobeta/android/dslv/SimpleFloatViewManager.java",
    "content": "package com.mobeta.android.dslv;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Point;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.ListView;\n\n/**\n * Simple implementation of the FloatViewManager class. Uses list\n * items as they appear in the ListView to create the floating View.\n */\npublic class SimpleFloatViewManager implements DragSortListView.FloatViewManager {\n\n\tprivate Bitmap mFloatBitmap;\n\n\tprivate ImageView mImageView;\n\n\tprivate int mFloatBGColor = Color.BLACK;\n\n\tprivate ListView mListView;\n\n\tpublic SimpleFloatViewManager(ListView lv) {\n\t\tmListView = lv;\n\t}\n\n\tpublic void setBackgroundColor(int color) {\n\t\tmFloatBGColor = color;\n\t}\n\n\t/**\n\t * This simple implementation creates a Bitmap copy of the\n\t * list item currently shown at ListView <code>position</code>.\n\t */\n\t@Override\n\tpublic View onCreateFloatView(int position) {\n\t\t// Guaranteed that this will not be null? I think so. Nope, got\n\t\t// a NullPointerException once...\n\t\tView v = mListView.getChildAt(position + mListView.getHeaderViewsCount() - mListView.getFirstVisiblePosition());\n\n\t\tif (v == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tv.setPressed(false);\n\n\t\t// Create a copy of the drawing cache so that it does not get\n\t\t// recycled by the framework when the list tries to clean up memory\n\t\t//v.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);\n\t\tv.setDrawingCacheEnabled(true);\n\n\t\t// EDITED by CampelloManuel @ github\n\t\tif (v.getDrawingCache() == null) {\n\t\t\t// big views (for us, notes with ~90 lines) are too big to fit into the cache,\n\t\t\t// so getDrawingCache() will always return null, which causes a crash.\n\t\t\t// So we have to build a shorter \"thumbnail\" view for dragging the listitem\n\n\t\t\t// TODO seems like we had to pass a custom DragSortListView.FloatViewManager\n\t\t\t//  implementation. well, whatever. If you feel like wasting time, see\n\t\t\t//  DragSortListView.java line ~2430 where it explains how to subclass this.\n\n\t\t\t// To me this seems about as high as a note with 3 lines, which is good enough\n\t\t\tint maxHeightInDp = 120;\n\t\t\t// convert to pixels\n\t\t\tint maxHeightEquivPixels = (int) TypedValue.applyDimension(\n\t\t\t\t\tTypedValue.COMPLEX_UNIT_DIP, maxHeightInDp,\n\t\t\t\t\tmListView.getContext().getResources().getDisplayMetrics());\n\n\t\t\t// if needed, truncate the height to something that will make the view small\n\t\t\t// enough to fit into the cache\n\t\t\tint h = Math.min(v.getMeasuredHeight(), maxHeightEquivPixels);\n\t\t\tint w = v.getMeasuredWidth(); // the width is always OK, it seems\n\n\t\t\tBitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\n\t\t\tCanvas bitmapHolder = new Canvas(bitmap);\n\t\t\tv.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);\n\t\t\tv.draw(bitmapHolder);\n\n\t\t\t// we drew a smaller tumbnail of the view on this bitmap: set it as cache\n\t\t\tmFloatBitmap = bitmap;\n\t\t} else {\n\t\t\t// normal behavior of the library\n\t\t\tmFloatBitmap = Bitmap.createBitmap(v.getDrawingCache());\n\t\t}\n\t\tv.setDrawingCacheEnabled(false);\n\n\t\tif (mImageView == null) {\n\t\t\tmImageView = new ImageView(mListView.getContext());\n\t\t}\n\t\tmImageView.setBackgroundColor(mFloatBGColor);\n\t\tmImageView.setPadding(0, 0, 0, 0);\n\t\tmImageView.setImageBitmap(mFloatBitmap);\n\t\tmImageView.setLayoutParams(new ViewGroup.LayoutParams(v.getWidth(), v.getHeight()));\n\n\t\treturn mImageView;\n\t}\n\n\t/**\n\t * This does nothing\n\t */\n\t@Override\n\tpublic void onDragFloatView(View floatView, Point position, Point touch) {\n\t\t// do nothing\n\t}\n\n\t/**\n\t * Removes the Bitmap from the ImageView created in\n\t * onCreateFloatView() and tells the system to recycle it.\n\t */\n\t@Override\n\tpublic void onDestroyFloatView(View floatView) {\n\t\t((ImageView) floatView).setImageDrawable(null);\n\n\t\tmFloatBitmap.recycle();\n\t\tmFloatBitmap = null;\n\t}\n\n}\n\n"
  },
  {
    "path": "fastlane/Appfile",
    "content": "json_key_file(\"/path/to/serviceaccount.json\")\npackage_name(\"com.nononsenseapps.notepad.play\")\n"
  },
  {
    "path": "fastlane/Fastfile",
    "content": "# This file contains the fastlane.tools configuration\n# You can find the documentation at https://docs.fastlane.tools\n#\n# For a list of all available actions, check out\n#\n#     https://docs.fastlane.tools/actions\n#\n# For a list of all available plugins, check out\n#\n#     https://docs.fastlane.tools/plugins/available-plugins\n#\n\n# Uncomment the line if you want fastlane to automatically update itself\n# update_fastlane\n\ndefault_platform(:android)\n\nplatform :android do\n  desc \"Fetch latest version code from Google Play\"\n  lane :get_latest_version do\n    track_version_codes = []\n\n    # Fetch latest codes\n    track_version_codes += google_play_track_version_codes(track: \"production\")\n    #track_version_codes += google_play_track_version_codes(track: \"beta\")\n\n    track_version_codes.max\n  end\n\n  desc \"Bumps version code in gradle file\"\n  lane :bump_version_code do\n    path = '../app/build.gradle'\n    re = /versionCode\\s+(\\d+)/\n\n    last_version_code = get_latest_version\n\n    s = File.read(path)\n    if (s[re, 1].to_i <= last_version_code) then\n      s[re, 1] = (last_version_code + 1).to_s\n\n      f = File.new(path, 'w')\n      f.write(s)\n      f.close\n    end\n  end\n\n  desc \"Build play bundle\"\n  lane :build_play_bundle do\n    gradle(task: \"clean app:bundlePlayStore\")\n  end\n\n  desc \"Deploy a new version to the Google Play\"\n  lane :deploy do |options|\n    upload_to_play_store(\n      #mapping: \"app/build/outputs/mapping/play/mapping.txt\",\n      aab: \"app/build/outputs/bundle/playStore/app-playStore.aab\",\n      track: options[:track]\n    )\n  end\n\n  desc \"Validate deployment of a new version to the Google Play\"\n  lane :validate_deploy do |options|\n    upload_to_play_store(\n      #mapping: \"app/build/outputs/mapping/play/mapping.txt\",\n      aab: \"app/build/outputs/bundle/playStore/app-playStore.aab\",\n      track: options[:track],\n      validate_only: true\n    )\n  end\n\n  desc \"Build and deploy a new version to the Google Play\"\n  lane :build_and_deploy do |options|\n    # Keep same version for changelog\n    #bump_version_code\n    build_play_bundle\n    deploy(options)\n  end\n\n  desc \"Build and valdidate deployment of a new version to the Google Play\"\n  lane :build_and_validate do |options|\n    # Validation should never fail so bump version\n    bump_version_code\n    build_play_bundle\n    validate_deploy(options)\n  end\nend\n\n"
  },
  {
    "path": "fastlane/metadata/android/ar/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/ar/short_description.txt",
    "content": "مذكرة بسيطة و تطبيق مهام مع واجهة رائعة\n"
  },
  {
    "path": "fastlane/metadata/android/ar/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nАкценти:\n- Коригирайте синхронизирането на Google Tasks.\n- Коригирайте грешка, която би изтрила всички бележки по време на синхронизиране на Google Tasks.\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/70000.txt",
    "content": "v7.0.0: NoNonsense Notes\n\nАкценти:\n- вече съвместим с Android 12\n- пуснете синхронизирането на Google Tasks\n- ORG и JSON файловете вече могат да се записват на няколко избрани места. Можете да изберете кое.\n\nПодробности:\n- повишен целеви SDK до версия 32\n- надстроени до AndroidX библиотеки\n- фиксирани всички тестове\n- надстроени зависимости и Gradle\n- общо почистване на кода\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71000.txt",
    "content": "NoNonsense Notes v7.1.0\n\nАкценти:\n- Подобрен избор на файлове за архивни файлове\n- Преместени ORG файлове в папка по подразбиране\n- Актуализирани преводи с помощта на Weblate\n- Поправена е грешка, която клонира напомняния при повторно отваряне на бележки\n\nПодробности:\n- Целеви Android 13\n- UI ощипвания\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71100.txt",
    "content": "﻿Nonsense Notes v7.1.1\n\nАкценти:\n- Преведено с помощта на Weblate\n- добавено глобално предпочитание за синхронизиране\n- променени разрешения\n- иконата за бърз достъп вече е видима в \"обикновен стартер\"\n- блокирайте напомнянията за редактиране на бележки, когато е заключено\n- коригиране на грешка, показваща празна бележка при споделяне на връзка\n\nПодробности:\n- малки подобрения на потребителския интерфейс\n- премахване на неизползван код\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71200.txt",
    "content": "NoNonsense Notes v7.1.2\n\nАкценти:\n- Преведено с помощта на Weblate\n- гласовото търсене работи отново\n- повече формати за дата, от които да избирате\n- показване на \"следващ месец\" и година, когато задачите са подредени по дата\n- поддръжка за автоматично архивиране на Android\n\nПодробности:\n- UI ощипвания за диалогови прозорци\n- корекции на грешки\n- премахна всички остарели кодове на Google задачи, това трябва да скрие досадния диалогов прозорец на акаунта в Google\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71300.txt",
    "content": "NoNonsense Notes v7.1.3\n\nАкценти:\n- Преведено с помощта на Weblate\n- добавете онлайн урок\n- спрете да питате за достъп до акаунт в Google\n\nПодробности:\n- UI ощипвания\n- почистване на кода\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71500.txt",
    "content": "NoNonsense Notes v7.1.5\n\nАкценти:\n- Актуална корекция за публикация в Play Store\n- Актуализирани зависимости\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71600.txt",
    "content": "NoNonsense Notes v7.1.6\n\nАкценти:\n- Преведено с помощта на Weblate\n- Фиксиран идентификатор на приложение за f-droid\n\nПодробности:\n- актуализиран град\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71700.txt",
    "content": "﻿Nonsense Notes v7.1.7\n\nАкценти:\n- Преведено с помощта на Weblate\n- актуализирано приложение и библиотеки за насочване към Android 14\n- показване на анимации на приложения само ако е активирано в системните настройки\n- направете редовете на менюто на чекмеджето по-компактни\n\nПодробности:\n- Използвайте java 17 в github действия\n- корекции на грешки и подобрения на стабилността\n- подобрена стабилност на тестовете\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71800.txt",
    "content": "﻿Nonsense Notes v7.1.8\n\nАкценти:\n- Актуализирани преводи с помощта на Weblate\n- Актуализирани зависимости и инструменти за изграждане\n- Преоразмерете текста в някои елементи на потребителския интерфейс, за да избегнете изрязването му\n- Актуализирана инфраструктура за автоматизирано тестване\n- Общи подобрения на стабилността\n- Фиксиран #509, така че сега повтарящите се напомняния могат да се справят с превключването между DST и ST, когато са пренасрочени\n"
  },
  {
    "path": "fastlane/metadata/android/bg/changelogs/71900.txt",
    "content": "﻿Nonsense Notes v7.1.9\n\nАкценти:\n- Преведено с помощта на Weblate\n- актуализирани зависимости и gradle\n- поправена грешка, причиняваща изчезване на бележките при сортиране по дата, виж #525\n"
  },
  {
    "path": "fastlane/metadata/android/bg/full_description.txt",
    "content": "Приложение за бележки и списък със задачи с отворен код, използващо най-новите технологии за Android.\n\n* Поддръжка на часовник на таблото (да, все още работи)\n* Известия: задайте напомняния в определен час, с опционално повторение в определени дни от седмицата\n* Избор на език\n* Джаджа с поддръжка на заключен екран, с възможност за преоразмеряване и конфигуриране\n* Защитете с парола важни бележки\n* Преместване на задачи между различни списъци\n* Плъзнете, за да пренаредите задачите\n* Интеграция с джаджа за Android Agenda\n* Търсене\n* Използвайте емотикони като тагове 🏷️\n* Светли и тъмни теми\n* Архивиране (импортиране и експортиране) на бележки в JSON файл\n* Синхронизиране с ORG файл в локалното хранилище\n* бележка история с всички предишни версии\n* кошче за изтрити бележки\n\nЗа да подобрите това приложение, направете дарение или просто споделете идеи,\nпосетете https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/bg/short_description.txt",
    "content": "Просто приложение за бележки и задачи с отлична навигация\n"
  },
  {
    "path": "fastlane/metadata/android/bg/title.txt",
    "content": "Бележки\n"
  },
  {
    "path": "fastlane/metadata/android/cs-CZ/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/cs-CZ/short_description.txt",
    "content": "Poznámkový blok a úkoly s podporou synchronizace\n"
  },
  {
    "path": "fastlane/metadata/android/cs-CZ/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/de-DE/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nHöhepunkte:\n- Google Tasks Synronisierung repariert.\n- Fehler behoben welcher alle Notizen beim syncronisieren mit Google Tasks gelöscht hätte.\n"
  },
  {
    "path": "fastlane/metadata/android/de-DE/full_description.txt",
    "content": "Open-Source-Notizblock und To-Do-Liste App mit den neuesten Android-Technologien.\n\n* Unterstützung der Dash Clock (ja, sie funktioniert noch)\n* Benachrichtigungen: Setzen Sie Erinnerungen zu einer bestimmten Zeit, mit optionaler Wiederholung an bestimmten Wochentagen\n* Sprachauswahl\n* Widget, mit Unterstützung für den Sperrbildschirm, in der Größe veränderbar und konfigurierbar.\n* Passwortschutz für wichtige Notizen\n* Verschieben von Aufgaben zwischen verschiedenen Listen\n* Ziehen, um Aufgaben neu zu ordnen\n* Integration mit Android Agenda Widget\n* Suche\n* Helle und dunkle Themen\n* Sichern (Import und Export) von Notizen in einer JSON-Datei\n* Synchronisierung mit einer ORG-Datei auf dem lokalen Speicher\n* Notizenverlauf mit allen vorherigen Versionen\n* Mülleimer für gelöschte Notizen\n\nZum verbessern dieser App, spenden oder teilen Sie einfach Ihre Ideen mit.\nBesuchen Sie uns unter https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/de-DE/short_description.txt",
    "content": "Notizblock und To-do-App mit Synchronisationsunterstützung\n"
  },
  {
    "path": "fastlane/metadata/android/de-DE/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nHighlights:\n- Fix Google Tasks sync.\n- Fix bug which would delete all notes during Google Tasks sync.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/70000.txt",
    "content": "v7.0.0: NoNonsense Notes\n\nHighlights:\n- now compatible with Android 12\n- drop Google Tasks synchronization\n- ORG and JSON files can now be saved in a few selected places. You can choose which.\n\nDetails:\n- raised target SDK to version 32\n- upgraded to AndroidX libraries\n- fixed all tests\n- upgraded dependencies and Gradle\n- general code cleanup\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71000.txt",
    "content": "NoNonsense Notes v7.1.0\n\nHighlights:\n- Improved file-picker for backup files\n- Moved ORG files to a default folder\n- Updated translations using Weblate\n- Fixed a bug that cloned reminders when re-opening notes\n\nDetails:\n- Target Android 13\n- UI tweaks\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71100.txt",
    "content": "﻿NoNonsense Notes v7.1.1\n\nHighlights:\n- Translated using Weblate\n- added global sync preference, can prevent sync code from running at all\n- changed permissions\n- shortcut icon is now visibile in \"simple launcher\"\n- block editing note reminders when it's locked\n- fix bug showing an empty note when sharing a link\n\nDetails:\n- small UI improvements\n- remove unused code"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71200.txt",
    "content": "NoNonsense Notes v7.1.2\n\nHighlights:\n- Translated using Weblate\n- voice search works again\n- more date formats to choose from\n- show \"next month\" and year when tasks are ordered by date\n- support for android auto backup\n\nDetails:\n- UI tweaks for dialogs\n- bug fixes\n- removed all obsolete google tasks code, this should hide the annoying google account dialog"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71300.txt",
    "content": "NoNonsense Notes v7.1.3\n\nHighlights:\n- Translated using Weblate\n- add an online tutorial\n- stop asking for google account access\n\nDetails:\n- UI tweaks\n- code cleanup"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71400.txt",
    "content": "NoNonsense Notes v7.1.4\n\nHighlights:\n- updated translations\n- show animations only if they are enabled in system settings #501\n- fix bug causing snoozed reminder to reappear\n- hide complete on notifications for note-types, fixes #312\n- show completed notes on widget\n- allow installing app from many app stores simultaneously\n- fix bug hiding search suggestions\n\nDetails:\n- added fastlane deployment pipeline to Play store. Pipeline only runs on tags\n- update dependencies\n- code cleanup\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71500.txt",
    "content": "NoNonsense Notes v7.1.5\n\nHighlights:\n- Hotfix for play store publication\n- Updated dependencies\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71600.txt",
    "content": "NoNonsense Notes v7.1.6\n\nHighlights:\n- Translated using Weblate\n- Fixed app ID for f-droid\n\nDetails:\n- updated gradle\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71700.txt",
    "content": "﻿NoNonsense Notes v7.1.7\n\nHighlights:\n- Translated using Weblate\n- updated app and libraries to target android 14\n- show app animations only if enabled in system settings\n- make drawer menu rows more compact\n\nDetails:\n- Use java 17 in github actions\n- bug fixes and stability improvements\n- improved stability of tests"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71800.txt",
    "content": "﻿NoNonsense Notes v7.1.8\n\nHighlights:\n- Updated translations using Weblate\n- Updated dependencies and build tools\n- Resize text in some UI elements to avoid it being cut\n- Updated automated testing infrastructure\n- General stability improvements\n- Fixed #509 so now repeating remindes can handle the switch between DST and ST when they are rescheduled"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/71900.txt",
    "content": "﻿NoNonsense Notes v7.1.9\n\nHighlights:\n- Translated using Weblate\n- updated dependencies and gradle\n- fixed a bug causing the notes to disappear when sorting by date, see #525\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/72000.txt",
    "content": "﻿NoNonsense Notes v7.2.0\n\nHighlights:\n- Updated translations using Weblate\n- Added official support for Android 15 (API 35)\n- Tweaked the layout of the \"note history\" page"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/72100.txt",
    "content": "NoNonsense Notes v7.2.1\n\nHighlights:\n- updated dependencies\n- added new languages\n- updated translations using Weblate\n- added the required backup file name in the import error message\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/72200.txt",
    "content": "NoNonsense Notes v7.2.2\n\nHotfix:\n* deleted co from fastlane because the play store does not support it. Corsican is available as a language in the app\n\nAnd everything from v7.2.1\n* updated dependencies\n* added new languages\n* updated translations using Weblate\n* added the required backup file name in the import error message\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/72300.txt",
    "content": "NoNonsense Notes v7.2.3\n\nHighlights:\n- finished the tutorial at https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md\n- fixed the shortcut widget for newer android versions\n- fixed bugs related to the note list widget\n- added official support for Android 16\n- improved how dates are shown in the \"search results\" and \"archived notes\" views\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/72600.txt",
    "content": "NoNonsense Notes v7.2.6\n\nHighlights:\n- finished the tutorial at https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md\n- fixed the shortcut widget for newer android versions\n- fixed bugs related to the note list widget\n- added official support for Android 16\n- improved how dates are shown in the \"search results\" and \"archived notes\" views\n- update app build infrastructure"
  },
  {
    "path": "fastlane/metadata/android/en-US/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Use emojis as tags 🏷️\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/short_description.txt",
    "content": "A simple notepad and to-do app with an excellent navigation\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nReflejos:\n- Arreglar la sincronización de tareas de Google.\n- Solucione el error que eliminaría todas las notas durante la sincronización de Google Tasks.\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/70000.txt",
    "content": "v7.0.0: NoNonsense Notes\n\nReflejos:\n- Ahora compatible con Android 12\n- Sincronización de Google Tasks caída\n- Los archivos ORG y JSON ahora se pueden guardar en varios lugares. Puedes elegir cuales.\n\nDetalles:\n- Target SDK aumentó a la versión 32\n- Actualizado a la biblioteca de AndroidX\n- Se corrigieron todas las pruebas.\n- Dependencias actualizadas y Gradle\n- Limpieza general de código\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71000.txt",
    "content": "NoNonsense Notes v7.1.0\n\nReflejos:\n- Selector de archivos mejorado para archivos de copia de seguridad\n- Archivos ORG movidos a una carpeta predeterminada\n- Traducciones actualizadas usando Weblate\n- Se corrigió un error que clonaba recordatorios al volver a abrir notas\n\nDetalles:\n- Objetivo Androide 13\n- Ajustes de interfaz de usuario\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71100.txt",
    "content": "NoNonsense Notes v7.1.1\n\nReflejos:\n- Traducido usando Weblate\n- preferencia de sincronización global agregada, puede evitar que el código de sincronización se ejecute en absoluto\n- permisos cambiados\n- El icono de acceso directo ahora es visible en \"lanzador simple\"\n- bloquear la edición de recordatorios de notas cuando está bloqueado\n- corrige el error que muestra una nota vacía al compartir un enlace\n\nDetalles:\n- pequeñas mejoras en la interfaz de usuario\n- eliminar el código no utilizado\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71200.txt",
    "content": "NoNonsense Notes v7.1.2\n\nReflejos:\n- Traducido usando Weblate\n- la búsqueda por voz vuelve a funcionar\n- más formatos de fecha para elegir\n- mostrar \"el próximo mes\" y el año cuando las tareas se ordenan por fecha\n- soporte para copia de seguridad automática de Android\n\nDetalles:\n- Ajustes de interfaz de usuario para cuadros de diálogo.\n- corrección de errores\n- Se eliminó todo el código obsoleto de las tareas de Google, esto debería ocultar el molesto cuadro de diálogo de la cuenta de Google\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71300.txt",
    "content": "NoNonsense Notes v7.1.3\n\nReflejos:\n- Traducido usando Weblate\n- agregar un tutorial en línea\n- deja de pedir acceso a la cuenta de google\n\nDetalles:\n- Ajustes de interfaz de usuario\n- limpieza de código\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71400.txt",
    "content": "NoNonsense Notes v7.1.4\n\nLo más destacado:\n- traducciones actualizadas\n- mostrar animaciones sólo si están habilitadas en la configuración del sistema #501\n- corregido error que causaba la reaparición del recordatorio snoozed\n- ocultar completo en las notificaciones para los tipos de notas, corrige #312\n- mostrar notas completadas en el widget\n- permitir la instalación de aplicaciones desde varias tiendas de aplicaciones simultáneamente\n- corregir error al ocultar las sugerencias de búsqueda\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71500.txt",
    "content": "Notas NoNonsense v7.1.5\n\nDestacados:\n- Hotfix para la publicación en play store\n- Dependencias actualizadas\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71600.txt",
    "content": "NoNonsense Notes v7.1.6\n\nLo más destacado:\n- Traducido usando Weblate\n- ID de aplicación fija para f-droid\n\nDetalles:\n- gradle actualizado\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71700.txt",
    "content": "﻿NoNonsense Notes v7.1.7\n\nLo más destacado:\n- Traducido usando Weblate\n- app y librerias actualizadas para android 14\n- mostrar animaciones de aplicaciones sólo si está habilitado en la configuración del sistema\n- hacer filas de menú cajón más compacto\n\nDetalles:\n- Utilice java 17 en las acciones de github\n- correcciones de errores y mejoras de estabilidad\n- mejora de la estabilidad de las pruebas\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71800.txt",
    "content": "﻿NoNonsense Notes v7.1.8\n\nDestacados:\n- Traducciones actualizadas usando Weblate\n- Dependencias y herramientas de compilación actualizadas\n- Redimensionado del texto en algunos elementos de la interfaz de usuario para evitar que se corte\n- Infraestructura de pruebas automatizadas actualizada\n- Mejoras generales de estabilidad\n- Corregido el #509 para que ahora los recordatorios repetitivos puedan manejar el cambio entre DST y ST cuando son reprogramados\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/changelogs/71900.txt",
    "content": "﻿NoNonsense Notes v7.1.9\n\nReflejos:\n- Traducido mediante Weblate\n- Dependencias y gradle actualizados\n- Se corrigió un error que causaba que las notas desaparecieran al ordenarlas por fecha, consulte el n.° 525\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/full_description.txt",
    "content": "Bloc de notas de código abierto y aplicación de lista de tareas utilizando las últimas tecnologías de Android.\n\n* Soporte para reloj (sí, todavía funciona)\n* Notificaciones: establece recordatorios a una hora determinada, con repetición opcional en determinados días de la semana.\n* Selección de idioma\n* Widget, compatible con la pantalla de bloqueo, redimensionable y configurable.\n* Proteger con contraseña las notas importantes\n* Mover tareas entre diferentes listas\n* Arrastrar para reordenar tareas\n* Integración con el widget Agenda de Android\n* Búsqueda\n* Temas claros y oscuros\n* Copia de seguridad (importación y exportación) de notas a un archivo JSON\n* Sincronización con un archivo ORG en el almacenamiento local\n* Historial de notas con todas las versiones anteriores\n* papelera de reciclaje para notas borradas\n\nPara mejorar esta aplicación, hacer una donación, o simplemente compartir ideas,\nvisítanos en https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/short_description.txt",
    "content": "App de bloc de notas y tareas pendientes con soporte a sincronización\n"
  },
  {
    "path": "fastlane/metadata/android/es-ES/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/fi-FI/changelogs/71400.txt",
    "content": "NoNonsense Notes v7.1.4\n\nKohokohdat:\n- päivitetyt käännökset\n- näytä animaatioita vain, jos ne on otettu käyttöön järjestelmäasetuksissa #501\n- korjaa vian, joka aiheuttaa torkkumuistutuksen ilmestymisen uudelleen\n- piilota täydellinen muistiinpanotyyppien ilmoituksissa, korjaukset #312\n- näytä valmiit muistiinpanot vimpaimessa\n- mahdollistaa sovellusten asentamisen useista sovelluskaupoista samanaikaisesti\n- Korjaa vika piilottamalla hakuehdotukset\n\nYksityiskohdat:\n- lisätty fastlanen käyttöönottoputki Play-kauppaan. Putkilinja toimii vain tunnisteilla\n- päivitä riippuvuudet\n- koodin puhdistus\n"
  },
  {
    "path": "fastlane/metadata/android/fi-FI/full_description.txt",
    "content": "Avoimen lähdekoodin muistilehtiö ja tehtävälistasovellus uusimmilla Android-tekniikoilla.\n\n* Kojelaudan kellon tuki (kyllä, se toimii edelleen)\n* Ilmoitukset: aseta muistutuksia tiettyyn aikaan ja valinnainen toisto tiettyinä arkipäivinä\n* Kielen valinta\n* Vimpain, lukitusnäytön tuella, kokoa muutettava ja konfiguroitavissa\n* Suojaa tärkeät huomautukset salasanalla\n* Siirrä tehtäviä eri luetteloiden välillä\n* Järjestä tehtäviä vetämällä\n* Integrointi Android Agenda -vimpaimeen\n* Haku\n* Vaaleat ja tummat teemat\n* Varmuuskopioi (tuonti ja vienti) muistiinpanot JSON-tiedostoon\n* Synkronoi paikallisessa tallennustilassa olevan ORG-tiedoston kanssa\n* Huomaa historia kaikkien aiempien versioiden kanssa\n* roskakori poistetuille muistiinpanoille\n\nParantaaksesi tätä sovellusta, tee lahjoitus tai yksinkertaisesti jaa ideoita,\ntule käymään osoitteessa https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/fi-FI/short_description.txt",
    "content": "A simple notepad and to-do app with an excellent navigation\n"
  },
  {
    "path": "fastlane/metadata/android/fi-FI/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/fr-FR/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications : set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/fr-FR/short_description.txt",
    "content": "Bloc-notes avec synchronisation\n"
  },
  {
    "path": "fastlane/metadata/android/fr-FR/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/he/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/he/short_description.txt",
    "content": "יישומון פנקס ורשימת מטלות עם תמיכה בסנכרון\n"
  },
  {
    "path": "fastlane/metadata/android/he/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/id/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nSorotan: \n- Memperbaiki sinkronisasi Google Tasks \n- Memperbaiki bug yang akan menghapus semua catatan selama sinkronisasi Google Tasks.\n"
  },
  {
    "path": "fastlane/metadata/android/id/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/id/short_description.txt",
    "content": "Aplikasi notepad dan daftar tugas yang sederhana dengan navigasi yang luar biasa\n"
  },
  {
    "path": "fastlane/metadata/android/id/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/is/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/is/short_description.txt",
    "content": "Minnisblokk og verkefnalistaforrit með samstillingareiginleikum\n"
  },
  {
    "path": "fastlane/metadata/android/is/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/it-IT/full_description.txt",
    "content": "Aggiornata per supportare la Android 13, quest'applicazione open source vanta le seguenti funzioni:\n\n* Supporto per Dash Clock (si, funziona ancora)\n* Notifiche, anche con ripetizione settimanale\n* Selezione della lingua preferita\n* Widget ridimensionabile e configurabile per la schermata home\n* Protezione delle note con password\n* Liste personalizzate tra le quali si possono spostare le note\n* Liste dove si possono riordinare a piacere le note\n* Integrazione con il widget Android Agenda\n* Ricerca tra le note\n* Usa le emoji come tag 🏷️\n* Disponibili temi chiari e scuri\n* Importazione & esportazione delle note su file JSON\n* Sincronizzazione con file ORG nella memoria locale\n* Cronologia nota\n* Elenco di note eliminate\n\nSe vuoi migliorare quest'app, fare una donazione o condividere qualche idea,\nvisita la nostra pagina su github: https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/it-IT/short_description.txt",
    "content": "Un semplice taccuino e lista di cose da fare con una navigazione eccellente\n"
  },
  {
    "path": "fastlane/metadata/android/it-IT/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/nb-NO/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nHøydepunkter:\n- Fikset Google Tasks-synkronisering.\n- Fikset feil som ville slettet alle notater under Google Tasks-synkronisering.\n"
  },
  {
    "path": "fastlane/metadata/android/nb-NO/changelogs/70000.txt",
    "content": "v7.0.0: NoNonsense Notes\n\nHøydepunkter:\n- Nå kompatibelt med Android 12\n- Droppet Google Tasks-synkronisering\n- ORG og JSON-filer kan nå lagres en del steder. Du kan velge hvilke.\n\nDetaljer:\n- Mål-SDK økt til versjon 32\n- Oppgradert til AndroidX-bibliotek\n- Fikset alle tester\n- Oppgraderte avhengigheter og Gradle\n- Generell kodeopprenskning\n"
  },
  {
    "path": "fastlane/metadata/android/nb-NO/changelogs/71000.txt",
    "content": "NoNonsense-notater v7.1.0\n\nHøydepunkter:\n- Forbedret filvelger for sikkerhetskopifiler\n- ORG-filer flyttet til forvalgt mappe\n- Oppdaterte oversettelser fra Weblate\n- Fikset feil som klonet påminnelser ved gjenåpning av notater\n\nDetaljer:\n- Innrettet mot Android 13\n- Grensesnittsforbedringer\n"
  },
  {
    "path": "fastlane/metadata/android/nb-NO/full_description.txt",
    "content": "Fri notatblokk og gjøremålspåminner som bruker de siste Android-teknologiene.\n\n* Dash-klokke-støtte (ja, det virker enda)\n* Merknader: Sett påminnelser på gitte tidspunkt, med valgfri gjentagelse de ukedagene du vil\n* Språkvalg\n* Miniprogram med låseskjermsstøtte, der størrelse og andre ting kan endres\n* Passordbeskytting av viktige notater\n* Flytt gjøremål mellom forskjellige lister\n* Dra for å endre gjøremålsrekkefølge\n* Integrasjon med Android Agenda-miniprogram\n* Søk\n* Lys og mørk drakt\n* Sikkerhetskopier (import og eksport) notater til JSON-fil\n* Synkroniser med en ORG-fil i lokalt lager\n\n\n\nForbedre programmet, eller del idéer.\nBesøk på https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/nb-NO/short_description.txt",
    "content": "Notisprogram med gjøremål og synkronisering\n"
  },
  {
    "path": "fastlane/metadata/android/nb-NO/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nNajważniejsze funkcje:\n- Naprawiono synchronizację zadań Google.\n- Naprawiono błąd powodujący usuwanie wszystkich notatek podczas synchronizacji z Google Tasks.\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/70000.txt",
    "content": "v7.0.0: Notatki NoNonsense\n\nNajważniejsze funkcje:\n- teraz kompatybilny z Androidem 12\n- synchronizacja z Drop Google Tasks\n- Pliki ORG i JSON można teraz zapisywać w kilku wybranych miejscach. Możesz wybrać, które.\n\nSzczegóły:\n- podniesiono docelowe SDK do wersji 32\n- zaktualizowano do bibliotek AndroidX\n- poprawiono wszystkie testy\n- zaktualizowane zależności i Gradle\n- ogólne czyszczenie kodu\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71000.txt",
    "content": "NoNonsense Notes v7.1.0\n\nNajważniejsze funkcje:\n- Ulepszona wyszukiwarka plików kopii zapasowych\n- Przeniesiono pliki ORG do domyślnego folderu\n- Zaktualizowano tłumaczenia przy użyciu Weblate\n- Naprawiono błąd powodujący klonowanie przypomnień podczas ponownego otwierania notatek.\n\nSzczegóły:\n- Docelowy Android 13\n- Poprawki interfejsu użytkownika\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71100.txt",
    "content": "NoNonsense Notes v7.1.1\n\nNajważniejsze informacje:\n- Przetłumaczone przy użyciu Weblate\n- dodano globalne preferencje synchronizacji, które mogą w ogóle uniemożliwić uruchamianie kodu synchronizacji\n- zmienione uprawnienia\n- ikona skrótu jest teraz widoczna w \"prostym programie uruchamiającym\"\n- zablokowano przypomnienia o edycji notatek, gdy są zablokowane\n- naprawiono błąd pokazujący pustą notatkę podczas udostępniania linku\n\nSzczegóły:\n- drobne usprawnienia interfejsu użytkownika\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71200.txt",
    "content": "NoNonsense Notes v7.1.2\n\nNajważniejsze informacje:\n- Przetłumaczone przy użyciu Weblate\n- wyszukiwanie głosowe znów działa\n- więcej formatów daty do wyboru\n- wyświetlanie \"następnego miesiąca\" i roku, gdy zadania są uporządkowane według daty\n- wsparcie dla automatycznej kopii zapasowej Android\n\nSzczegóły:\n- Poprawki interfejsu użytkownika dla okien dialogowych\n- poprawki błędów\n- usunięto cały przestarzały kod zadań Google, powinno to ukryć irytujące okno dialogowe konta Google\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71300.txt",
    "content": "NoNonsense Notes v7.1.3\n\nNajważniejsze informacje:\n- Przetłumaczone przy użyciu Weblate\n- dodano samouczek online\n- przestał prosić o dostęp do konta Google\n\nSzczegóły:\n- Poprawki interfejsu użytkownika\n- czyszczenie kodu\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71400.txt",
    "content": "NoNonsense Notes v7.1.4\n\nNajważniejsze:\n- aktualizacja tłumaczeń\n- wyświetlanie animacji tylko wtedy, gdy są włączone w ustawieniach systemowych #501\n- naprawiono błąd powodujący pojawienie się odłożonego przypomnienia\n- ukrywanie ukończonych powiadomień dla typów notatek #312\n- notatki na widżecie\n- instalowanie aplikacji z wielu sklepów\n- naprawiono błąd ukrywający sugestie wyszukiwania\n\nSzczegóły:\n- dodano potok wdrażania fastlane do sklepu Play.\n- aktualizacja zależności\n- czyszczenie kodu\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71500.txt",
    "content": "NoNonsense Notes v7.1.5\n\nNajważniejsze informacje:\n- Poprawka dotycząca publikacji w sklepie Play\n- Zaktualizowano zależności\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71600.txt",
    "content": "NoNonsense Notes v7.1.6\n\nNajważniejsze informacje:\n- Przetłumaczone przy użyciu Weblate\n- Naprawiono identyfikator aplikacji dla f-droid\n\nSzczegóły:\n- zaktualizowano gradle\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/changelogs/71700.txt",
    "content": "NoNonsense Notes v7.1.7\n\nNajważniejsze informacje:\n- Przetłumaczone przy użyciu Weblate\n- Zaktualizowano aplikację i biblioteki do Androida 14\n- wyświetlanie animacji aplikacji tylko wtedy, gdy są włączone w ustawieniach systemowych\n- bardziej kompaktowe wiersze menu szuflady\n\nSzczegóły:\n- Używa java 17 w działaniach github\n- poprawki błędów i ulepszenia stabilności\n- poprawiona stabilność testów\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/full_description.txt",
    "content": "Notatnik open source i aplikacja z listą rzeczy do zrobienia wykorzystująca najnowsze technologie Androida.\n\n* Obsługa zegara Dash (tak, nadal działa)\n* Powiadomienia: ustawianie przypomnień o określonej godzinie, z opcjonalnym powtarzaniem w określone dni tygodnia\n* Wybór języka\n* Widżet z obsługą ekranu blokady, z możliwością zmiany rozmiaru i konfiguracji\n* Ochrona hasłem ważnych notatek\n* Przenoszenie zadań między różnymi listami\n* Przeciągnij, aby zmienić kolejność zadań\n* Integracja z widżetem Android Agenda\n* Wyszukiwanie\n* Jasne i ciemne motywy\n* Tworzenie kopii zapasowych (import i eksport) notatek do pliku JSON\n* Synchronizacja z plikiem ORG w lokalnej pamięci masowej\n* Historia notatek ze wszystkimi poprzednimi wersjami\n* kosz na usunięte notatki\n\nAby ulepszyć tę aplikację, przekazać darowiznę lub po prostu podzielić się pomysłami,\nodwiedź stronę https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/short_description.txt",
    "content": "Prosty notatnik i aplikacja z doskonałą nawigacją\n"
  },
  {
    "path": "fastlane/metadata/android/pl-PL/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/pt-BR/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/pt-BR/short_description.txt",
    "content": "Aplicativo de bloco de notas e de tarefas com suporte a sincronização\n"
  },
  {
    "path": "fastlane/metadata/android/pt-BR/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/pt-PT/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/pt-PT/short_description.txt",
    "content": "App de bloco de notas e de tarefas com suporte a sincronização\n"
  },
  {
    "path": "fastlane/metadata/android/pt-PT/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/ro/full_description.txt",
    "content": "Open source notepad and to-do list app using the latest Android technologies.\n\n* Dash clock support (yes, it still works)\n* Notifications: set reminders at a certain time, with optional repetition on certain weekdays\n* Language selection\n* Widget, with lockscreen support, resizeable and configurable\n* Password-protect important notes\n* Move tasks between different lists\n* Drag to reorder tasks\n* Integration with Android Agenda widget\n* Search\n* Light and dark themes\n* Back up (import and export) notes to a JSON file\n* Sync with an ORG file on the local storage\n* note history with all previous versions\n* recycle bin for deleted notes\n\nTo improve this app, make a donation, or simply share ideas,\ncome visit at https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/ro/short_description.txt",
    "content": "Blocul de notițe și aplicația Todo cu suport pentru sincronizare\n"
  },
  {
    "path": "fastlane/metadata/android/ro/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nОсновные изменения:\n- Исправлена синхронизация с Google Tasks.\n- Исправлена ошибка, из-за которой все заметки удалялись при синхронизации с Google Tasks.\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/70000.txt",
    "content": "v7.0.0: NoNonsense Notes\n\nОсновные изменения:\n- Теперь совместимо с Android 12\n- Убрана синхронизация с Google Tasks\n- Файлы ORG и JSON теперь можно сохранять в нескольких выбранных местах. Вы можете выбрать, где.\n\nДетали:\n- Повышен целевой SDK до версии 32\n- Обновлены библиотеки AndroidX\n- Исправлены все тесты\n- Обновлены зависимости и Gradle\n- Общая очистка кода\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71000.txt",
    "content": "NoNonsense Notes v7.1.0\n\nОсновные изменения:\n- Улучшен выбор файлов для резервных копий\n- Перемещены файлы ORG в папку по умолчанию\n- Обновлены переводы с использованием Weblate\n- Исправлена ошибка, из-за которой напоминания клонировались при повторном открытии заметок\n\nДетали:\n- Добавлена поддержка Android 13\n- Добавлены настройки интерфейса\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71100.txt",
    "content": "NoNonsense Notes v7.1.1\n\nОсновные изменения:\n- Переведено с использованием Weblate\n- Добавлено глобальное предпочтение синхронизации, которое может полностью предотвратить выполнение кода синхронизации\n- Изменены разрешения\n- Значок ярлыка теперь виден в «простой панели запуска»\n- Заблокировано редактирование напоминаний о заметках, когда они заблокированы\n- Исправлена ошибка с отображением пустой заметки при обмене ссылкой.\n\nДетали:\n- Небольшие улучшения интерфейса\n- Удалён неиспользуемый код\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71200.txt",
    "content": "NoNonsense Notes v7.1.2\n\nОсновные моменты:\n- Переведено с использованием Weblate\n- Голосовой поиск снова работает\n- Больше форматов даты на выбор\n- Отображение \"следующего месяца\" и года при сортировке задач по дате\n- Поддержка автоматического резервного копирования для Android\n\nДетали:\n- Небольшие изменения в интерфейсе диалогов\n- Исправления ошибок\n- Удалён весь устаревший код Google Tasks, это должно скрыть надоедливый диалог учётной записи Google\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71300.txt",
    "content": "NoNonsense Notes v7.1.3\n\nОсновные изменения:\n- Переведено с использованием Weblate\n- Добавлено руководство по использованию приложения\n- Прекращен запрос доступа к аккаунту Google\n\nДетали:\n- Улучшения интерфейса\n- Очистка кода\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71400.txt",
    "content": "NoNonsense Notes v7.1.4\n\nОсновные изменения:\n- Обновлены переводы\n- Отображение анимации только при включённых системных настройках #501\n- Исправлена ошибка, из-за которой отложенные напоминания появлялись снова\n- Скрытие завершённых уведомлений для типов заметок, исправлено #312\n- Отображение завершенной заметки в виджете\n- Разрешено устанавливать приложение из нескольких магазинов приложений одновременно\n- Исправлена ошибка, скрывающая предложения поиска\n\nДетали:\n- Добавлен pipeline развертывания fastlane в Play Store. Pipeline запускается только на тегах\n- Обновление зависимостей\n- Очистка кода\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71500.txt",
    "content": "NoNonsense Notes v7.1.5\n\nОсновные изменения:\n- Исправления для релиза в Play Store\n- Обновлены зависимости\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71600.txt",
    "content": "NoNonsense Notes v7.1.6\n\nОсновные изменения:\n- Переведено с помощью Weblate\n- Исправлен идентификатор приложения для f-droid\n\nДетали:\n- Обновлён gradle\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71700.txt",
    "content": "NoNonsense Notes v7.1.7\n\nОсновные изменения:\n- Переведено с использованием Weblate\n- Обновлено приложение и библиотеки для поддержки Android 14\n- Показ анимаций приложения только при включенных системных настройках\n- Сделаны строки меню более компактными\n\nДетали:\n- Использование Java 17 в действиях GitHub\n- Исправления ошибок и улучшения стабильности\n- Повышена стабильность тестов\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71800.txt",
    "content": "NoNonsense Notes v7.1.8\n\nОсновные изменения:\n- Обновлены переводы с использованием Weblate\n- Обновлены зависимости и инструменты сборки\n- Изменен размер текста в некоторых элементах интерфейса, чтобы избежать обрезки\n- Обновлена инфраструктура автоматизированного тестирования\n- Общие улучшения стабильности\n- Исправлена ошибка #509, теперь повторяющиеся напоминания могут корректно обрабатывать переход между летним и стандартным временем при переназначении\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/71900.txt",
    "content": "NoNonsense Notes v7.1.9\n\nОсновные изменения:\n- Переведено с помощью Weblate\n- Обновлены зависимости и Gradle\n- Исправлена ошибка, из-за которой заметки исчезали при сортировке по дате, см. #525\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/72000.txt",
    "content": "NoNonsense Notes v7.2.0\n\nОсновные изменения:\n- Обновлены переводы с использованием Weblate\n- Добавлена официальная поддержка Android 15 (API 35)\n- Подкорректирован макет страницы \"история заметок\"\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/72100.txt",
    "content": "NoNonsense Notes v7.2.1\n\nОсновные изменения:\n- Обновлены зависимости\n- Добавлены новые языки\n- Обновлены переводы с использованием Weblate\n- В сообщение об ошибке импорта добавлено имя требуемого файла резервной копии\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/72200.txt",
    "content": "NoNonsense Notes v7.2.2\n\nБыстрое исправление:\n* Удалён код 'co' из fastlane, так как Play Store его не поддерживает. При этом корсиканский язык по-прежнему доступен в самом приложении\n\nА также все изменения из версии v7.2.1\n* Обновлены зависимости\n* Добавлены новые языки\n* Обновлены переводы с использованием Weblate\n* В сообщение об ошибке импорта добавлено имя требуемого файла резервной копии\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/72300.txt",
    "content": "NoNonsense Notes v7.2.3\n\nОсновные изменения:\n- Завершено написание руководства по ссылке https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md\n- Исправлена работа виджета-ярлыка для новых версий Android\n- Исправлены ошибки, связанные с виджетом списка заметок\n- Добавлена официальная поддержка Android 16\n- Улучшено отображение дат в разделах \"результаты поиска\" и \"архивированные заметки\"\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/changelogs/72600.txt",
    "content": "NoNonsense Notes v7.2.6\n\nОсновные изменения:\n- Завершено написание руководства по ссылке https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md\n- Исправлена работа виджета-ярлыка для новых версий Android\n- Исправлены ошибки, связанные с виджетом списка заметок\n- Добавлена официальная поддержка Android 16\n- Улучшено отображение дат в разделах \"результаты поиска\" и \"архивированные заметки\"\n- Обновлена инфраструктура сборки приложения\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/full_description.txt",
    "content": "Приложение с открытым исходным кодом, объединяющее блокнот и список задач, созданное с использованием новейших технологий Android.\n\n* Поддержка DashClock (да, она всё ещё работает)\n* Уведомления: установка напоминаний на определённое время с возможностью повторения по выбранным дням недели\n* Выбор языка\n* Настраиваемый виджет с возможностью изменения размера и поддержкой экрана блокировки\n* Защита важных заметок паролем\n* Перемещение задач между разными списками\n* Изменение порядка задач перетаскиванием\n* Интеграция с виджетом Android Agenda\n* Поиск\n* Использование эмодзи в качестве тегов 🏷️\n* Светлая и тёмная темы\n* Резервное копирование (импорт и экспорт) заметок в JSON-файл\n* Синхронизация с ORG-файлом в локальном хранилище\n* История заметок, сохраняющая все предыдущие версии\n* Корзина для удалённых заметок\n\nЧтобы помочь улучшить приложение, сделать пожертвование или просто поделиться идеями,\nзаходите на https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/short_description.txt",
    "content": "Блокнот и Todo-приложение с поддержкой синхронизации\n"
  },
  {
    "path": "fastlane/metadata/android/ru-RU/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/57130.txt",
    "content": "v5.7.1: nononsonenset குறிப்புகள்\n\n சிறப்பம்சங்கள்:\n - Google பணிகள் ஒத்திசைவை சரிசெய்யவும்.\n - Google பணிகள் ஒத்திசைவின் போது எல்லா குறிப்புகளையும் நீக்கும் பிழையை சரிசெய்யவும்.\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/70000.txt",
    "content": "v7.0.0: nononsonensed குறிப்புகள்\n\n சிறப்பம்சங்கள்:\n - இப்போது ஆண்ட்ராய்டு 12 உடன் இணக்கமானது\n - Google பணிகள் ஒத்திசைவை விடுங்கள்\n - ORG மற்றும் சாதொபொகு கோப்புகளை இப்போது தேர்ந்தெடுக்கப்பட்ட சில இடங்களில் சேமிக்க முடியும். நீங்கள் எதை தேர்வு செய்யலாம்.\n\n விவரங்கள்:\n - இலக்கு SDK ஐ பதிப்பு 32 க்கு உயர்த்தியது\n - Androidx நூலகங்களுக்கு மேம்படுத்தப்பட்டது\n - அனைத்து சோதனைகளும் சரி செய்யப்பட்டன\n - மேம்படுத்தப்பட்ட சார்புநிலைகள் மற்றும் கிரேடில்\n - பொது குறியீடு தூய்மைப்படுத்துதல்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71000.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.0\n\n சிறப்பம்சங்கள்:\n - காப்பு கோப்புகளுக்கான மேம்படுத்தப்பட்ட கோப்பு-பிக்கர்\n - ஆர்க் கோப்புகளை இயல்புநிலை கோப்புறையில் நகர்த்தியது\n - வெப்லேட்டைப் பயன்படுத்தி புதுப்பிக்கப்பட்ட மொழிபெயர்ப்புகள்\n - குறிப்புகளை மீண்டும் திறக்கும் போது நினைவூட்டல்களை நகலி செய்த ஒரு பிழை சரி செய்யப்பட்டது\n\n விவரங்கள்:\n - இலக்கு ஆண்ட்ராய்டு 13\n - இடைமுகம் மாற்றங்கள்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71100.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.1\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி மொழிபெயர்க்கப்பட்டுள்ளது\n - உலகளாவிய ஒத்திசைவு விருப்பம் சேர்க்கப்பட்டால், ஒத்திசைவு குறியீடு இயங்குவதைத் தடுக்கலாம்\n - மாற்றப்பட்ட அனுமதிகள்\n - குறுக்குவழி படவுரு இப்போது \"சிம்பிள் லாஞ்சர்\" இல் பார்வைக்கு வருகிறது\n - குறிப்புக் குறிப்பு நினைவூட்டல்களை பூட்டும்போது தடுக்கவும்\n - இணைப்பைப் பகிரும்போது வெற்று குறிப்பைக் காட்டும் பிழையைச் சரிசெய்யவும்\n\n விவரங்கள்:\n - சிறிய இடைமுகம் மேம்பாடுகள்\n - பயன்படுத்தப்படாத குறியீட்டை அகற்று\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71200.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.2\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி மொழிபெயர்க்கப்பட்டுள்ளது\n - குரல் தேடல் மீண்டும் வேலை செய்கிறது\n - தேர்வு செய்ய அதிக தேதி வடிவங்கள்\n - \"அடுத்த மாதம்\" மற்றும் தேதியின்படி பணிகள் ஆர்டர் செய்யப்படும் ஆண்டைக் காட்டு\n - ஆண்ட்ராய்டு ஆட்டோ காப்புப்பிரதிக்கான ஆதரவு\n\n விவரங்கள்:\n - உரையாடல்களுக்கான இடைமுகம் மாற்றங்கள்\n - பிழை திருத்தங்கள்\n - வழக்கற்றுப் போன அனைத்து கூகிள் பணிகள் குறியீட்டையும் அகற்றி, இது எரிச்சலூட்டும் Google கணக்கு உரையாடலை மறைக்க வேண்டும்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71300.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.3\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி மொழிபெயர்க்கப்பட்டுள்ளது\n - நிகழ்நிலை டுடோரியலைச் சேர்க்கவும்\n - Google கணக்கு அணுகலைக் கேட்பதை நிறுத்துங்கள்\n\n விவரங்கள்:\n - இடைமுகம் மாற்றங்கள்\n - குறியீடு தூய்மைப்படுத்துதல்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71400.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.4\n\n சிறப்பம்சங்கள்:\n - புதுப்பிக்கப்பட்ட மொழிபெயர்ப்புகள்\n - அனிமேசன்கள் கணினி அமைப்புகள் #501 இல் இயக்கப்பட்டிருந்தால் மட்டுமே காண்பி\n - ச்னூச் நினைவூட்டல் மீண்டும் தோன்றும் பிழையைச் சரிசெய்யவும்\n - குறிப்பு வகைகளுக்கான அறிவிப்புகளில் முழுமையாக மறைக்கவும், திருத்தங்கள் #312\n - விட்செட்டில் நிறைவு செய்யப்பட்ட குறிப்புகளைக் காட்டு\n - ஒரே நேரத்தில் பல பயன்பாட்டு கடைகளிலிருந்து பயன்பாட்டை நிறுவ அனுமதிக்கவும்\n - பிழை மறைக்கும் தேடல் பரிந்துரைகளைச் சரிசெய்யவும்\n\n விவரங்கள்:\n - கடை விளையாட ஃபாச்ட்லேன் வரிசைப்படுத்தல் குழாய் சேர்க்கப்பட்டது. குழாய்த்திட்டங்கள் குறிச்சொற்களில் மட்டுமே இயங்குகின்றன\n - சார்புகளைப் புதுப்பிக்கவும்\n - குறியீடு தூய்மைப்படுத்துதல்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71500.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.5\n\n சிறப்பம்சங்கள்:\n - பிளே கடை வெளியீட்டிற்கான ஆட்ஃபிக்ச்\n - புதுப்பிக்கப்பட்ட சார்புகள்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71600.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.6\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி மொழிபெயர்க்கப்பட்டுள்ளது\n - எஃப்-டிராய்டுக்கான நிலையான பயன்பாட்டு ஐடி\n\n விவரங்கள்:\n - புதுப்பிக்கப்பட்ட கிரேடில்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71700.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.7\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி மொழிபெயர்க்கப்பட்டுள்ளது\n - ஆண்ட்ராய்டு 14 ஐ குறிவைக்க பயன்பாடு மற்றும் நூலகங்கள் புதுப்பிக்கப்பட்டன\n - கணினி அமைப்புகளில் இயக்கப்பட்டால் மட்டுமே பயன்பாட்டு அனிமேசன்களைக் காட்டு\n - டிராயர் பட்டியல் வரிசைகளை மேலும் கச்சிதமாக ஆக்குங்கள்\n\n விவரங்கள்:\n - அறிவிலிமையம் செயல்களில் சாவா 17 ஐப் பயன்படுத்தவும்\n - பிழை திருத்தங்கள் மற்றும் நிலைத்தன்மை மேம்பாடுகள்\n - சோதனைகளின் மேம்பட்ட நிலைத்தன்மை\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71800.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.8\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி புதுப்பிக்கப்பட்ட மொழிபெயர்ப்புகள்\n - புதுப்பிக்கப்பட்ட சார்புகள் மற்றும் கருவிகளை உருவாக்குங்கள்\n - வெட்டப்படுவதைத் தவிர்க்கச் சில இடைமுகம் கூறுகளில் உரையை மறுஅளவிடுங்கள்\n - புதுப்பிக்கப்பட்ட தானியங்கி சோதனை உள்கட்டமைப்பு\n - பொது நிலைத்தன்மை மேம்பாடுகள்\n - #509 சரி செய்யப்பட்டது, எனவே இப்போது மறுநிகழ்வு நினைவூட்டல்கள் DST மற்றும் ST க்கு இடையிலான மாற்றத்தை மறு திட்டமிடப்படும்போது கையாள முடியும்.\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/71900.txt",
    "content": "Nononsonence குறிப்புகள் v7.1.9\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி மொழிபெயர்க்கப்பட்டுள்ளது\n - புதுப்பிக்கப்பட்ட சார்புகள் மற்றும் கிரேடில்\n - தேதியால் வரிசைப்படுத்தும்போது குறிப்புகள் மறைந்துவிடும் ஒரு பிழை சரி செய்யப்பட்டது, #525 ஐப் பார்க்கவும்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/changelogs/72000.txt",
    "content": "Nononsonence குறிப்புகள் v7.2.0\n\n சிறப்பம்சங்கள்:\n - வெப்லேட்டைப் பயன்படுத்தி புதுப்பிக்கப்பட்ட மொழிபெயர்ப்புகள்\n - ஆண்ட்ராய்டு 15 க்கான அதிகாரப்பூர்வ உதவி சேர்க்கப்பட்டது (API 35)\n - \"குறிப்பு வரலாறு\" பக்கத்தின் தளவமைப்பை மாற்றியது\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/full_description.txt",
    "content": "அண்மைக் கால ஆண்ட்ராய்டு தொழில்நுட்பங்களைப் பயன்படுத்தி மூல நோட்பேட் மற்றும் செய்ய வேண்டிய பட்டியல் பயன்பாடு.\n\n * கோடு கடிகார உதவி (ஆம், அது இன்னும் வேலை செய்கிறது)\n * அறிவிப்புகள்: ஒரு குறிப்பிட்ட நேரத்தில் நினைவூட்டல்களை அமைக்கவும், சில வார நாட்களில் விருப்பமான மறுபடியும்\n * மொழி தேர்வு\n * விட்செட், பூட்டு திரை ஆதரவுடன், மறுபரிசீலனை செய்யக்கூடியது மற்றும் உள்ளமைக்கக்கூடியது\n * கடவுச்சொல்-பாதுகாப்பு முக்கியமான குறிப்புகள்\n * வெவ்வேறு பட்டியல்களுக்கு இடையில் பணிகளை நகர்த்தவும்\n * பணிகளை மறுதொடக்கம் செய்ய இழுக்கவும்\n * ஆண்ட்ராய்டு நிகழ்ச்சி நிரல் விட்செட்டுடன் ஒருங்கிணைப்பு\n * தேடுங்கள்\n * ஒளி மற்றும் இருண்ட கருப்பொருள்கள்\n * சாதொபொகு கோப்பில் குறிப்புகளை காப்புப் பிரதி எடுக்கவும் (இறக்குமதி மற்றும் ஏற்றுமதி)\n * உள்ளக சேமிப்பகத்தில் ஒரு org கோப்புடன் ஒத்திசைக்கவும்\n * முந்தைய அனைத்து பதிப்புகளுடன் வரலாற்றைக் கவனியுங்கள்\n * நீக்கப்பட்ட குறிப்புகளுக்கு மறுசுழற்சி பின்\n\n இந்த பயன்பாட்டை மேம்படுத்த, நன்கொடை அளிக்க அல்லது கருத்துக்களைப் பகிர்ந்து கொள்ளுங்கள்,\n https://github.com/spacecowboy/notepad இல் பார்வையிடவும்\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/short_description.txt",
    "content": "ஒரு சிறந்த வழிசெலுத்தல் கொண்ட எளிய நோட்பேட் மற்றும் செய்ய வேண்டிய பயன்பாடு\n"
  },
  {
    "path": "fastlane/metadata/android/ta-IN/title.txt",
    "content": "Nonnssense குறிப்புகள்\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/57130.txt",
    "content": "v5.7.1: NoNonsense Notes\n\nÖnemli noktalar:\n- Google Görevler eşitlemesi düzeltildi.\n- Google Görevler eşitlemesi sırasında tüm notların silinmesine neden olan hata düzeltildi.\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/70000.txt",
    "content": "v7.0.0: NoNonsense Notes\n\nÖnemli noktalar:\n- şimdi Android 12 ile uyumlu\n- Google Görevler eşitlemesi bırakıldı\n- ORG ve JSON dosyaları artık seçilen birkaç yere kaydedilebilir. Hangisi olduğunu seçebilirsiniz.\n\nAyrıntılar:\n- hedef SDK sürümü 32'ye yükseltildi\n- AndroidX kütüphanelerine yükseltildi\n- tüm testler düzeltildi\n- bağımlılıklar ve Gradle yükseltildi\n- genel kod temizliği\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71000.txt",
    "content": "NoNonsense Notes v7.1.0\n\nÖnemli noktalar:\n- Yedekleme dosyaları için dosya seçici iyileştirildi\n- ORG dosyaları öntanımlı bir klasöre taşındı\n- Çeviriler Weblate kullanılarak güncellendi\n- Notları yeniden açarken hatırlatıcıları klonlayan bir hata düzeltildi\n\nAyrıntılar:\n- Android 13 hedeflendi\n- Kullanıcı arayüzü iyileştirmeleri\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71100.txt",
    "content": "﻿NoNonsense Notes v7.1.1\n\nÖnemli noktalar:\n- Weblate kullanılarak çevrildi\n- genel eşitleme tercihi eklendi, eşitleme kodunun hiç çalışmasını engelleyebilir\n- izinler değiştirildi\n- kısayol simgesi artık \"basit başlatıcıda\" görülebilir\n- kilitli olduğunda not hatırlatıcılarının düzenlenmesi engellendi\n- bağlantı paylaşırken boş not gösterme hatası düzeltildi\n\nAyrıntılar:\n- küçük kullanıcı arayüzü iyileştirmeleri\n- kullanılmayan kodlar kaldırıldı\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71200.txt",
    "content": "NoNonsense Notes v7.1.2\n\nÖnemli noktalar:\n- Weblate kullanılarak çevrildi\n- sesli arama tekrar çalışıyor\n- aralarından seçim yapabileceğiniz daha fazla tarih biçimi\n- görevler tarihe göre sıralandığında \"gelecek ay\" ve yılı göster\n- android otomatik yedekleme desteği\n\nAyrıntılar:\n- İletişim kutuları için kullanıcı arayüzü iyileştirmeleri\n- hata düzeltmeleri\n- tüm eski google görevleri kodu kaldırıldı, bu can sıkıcı google hesabı iletişim kutusunu gizleyecektir\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71300.txt",
    "content": "NoNonsense Notes v7.1.3\n\nÖnemli noktalar:\n- Weblate kullanılarak çevrildi\n- çevrim içi eğitim eklendi\n- google hesabına erişim isteme bırakıldı\n\nAyrıntılar:\n- kullanıcı arayüzü iyileştirmeleri\n- kod temizliği\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71400.txt",
    "content": "NoNonsense Notes v7.1.4\n\nÖnemli noktalar:\n- çeviriler güncellendi\n- animasyonları yalnızca sistem ayarlarında etkinleştirilmişlerse göster #501\n- Ertelenen hatırlatıcının yeniden görünmesine neden olan hata düzeltildi\n- not türleri için bildirimlerde tamamlandı gizlendi, #312'yi düzeltir\n- widget'ta tamamlanan notları göster\n- aynı anda birçok uygulama mağazasından uygulama kurmaya izin ver\n- arama önerilerini gizleme hatası düzeltildi\n\nAyrıntılar:\n- Play Store'a fastlane dağıtım iş akışı eklendi. İş akışı yalnızca etiketlerde çalışır\n- Bağımlılıklar güncellendi\n- Kod temizliği yapıldı\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71500.txt",
    "content": "NoNonsense Notes v7.1.5\n\nÖnemli noktalar:\n- Play Store yayını için düzeltme\n- bağımlılıklar güncellendi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71600.txt",
    "content": "NoNonsense Notes v7.1.6\n\nÖnemli noktalar:\n- Weblate kullanılarak çevrildi\n- f-droid için uygulama kimliği düzeltildi\n\nAyrıntılar:\n- gradle güncellendi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71700.txt",
    "content": "﻿NoNonsense Notes v7.1.7\n\nÖnemli noktalar:\n- Weblate kullanılarak çevrildi\n- android 14'ü hedeflemek için uygulama ve kütüphaneler güncellendi\n- uygulama animasyonlarını yalnızca sistem ayarlarında etkinleştirilmişse göster\n- çekmece menü satırları daha derli toplu hale getirildi\n\nAyrıntılar:\n- Github eylemlerinde java 17 kullan\n- hata düzeltmeleri ve kararlılık iyileştirmeleri\n- testlerin kararlılığının iyileştirilmesi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71800.txt",
    "content": "﻿NoNonsense Notes v7.1.8\n\nÖnemli noktalar:\n- Çeviriler Weblate kullanılarak güncellendi\n- Bağımlılıklar ve derleme araçları güncellendi\n- Kesilmesini önlemek için bazı kullanıcı arayüzü ögelerindeki metin yeniden boyutlandırıldı\n- Otomatik test altyapısı güncellendi\n- Genel kararlılık iyileştirmeleri\n- #509 düzeltildi, böylece artık tekrar eden hatırlatmalar yeniden planlandıklarında DST ve ST arasındaki geçişi gerçekleştirebiliyor\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/71900.txt",
    "content": "﻿NoNonsense Notes v7.1.9\n\nÖnemli noktalar:\n- Weblate kullanılarak çevrildi\n- bağımlılıklar ve gradle güncellendi\n- tarihe göre sıralama yaparken notların kaybolmasına neden olan bir hata düzeltildi, bkz: #525\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/72000.txt",
    "content": "NoNonsense Notes v7.2.0\n\nÖnemli noktalar:\n- Weblate kullanılarak çeviriler güncellendi\n- Android 15 (API 35) için resmi destek eklendi\n- \"Not geçmişi\" sayfasının düzeni değiştirildi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/72100.txt",
    "content": "NoNonsense Notes v7.2.1\n\nÖnemli noktalar:\n- bağımlılıklar güncellendi\n- yeni diller eklendi\n- Weblate kullanılarak çeviriler güncellendi\n- içe aktarma hata mesajına gerekli yedekleme dosyası adı eklendi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/72200.txt",
    "content": "NoNonsense Notes v7.2.2\n\nDüzeltme:\n* Play Store desteklemediği için fastlane'den co dili silindi. Korsikaca, uygulamada dil olarak kullanılabilir.\n\nVe v7.2.1'deki her şey\n* bağımlılıklar güncellendi\n* yeni diller eklendi\n* Weblate kullanılarak çeviriler güncellendi\n* içe aktarma hata mesajına gerekli yedekleme dosyası adı eklendi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/72300.txt",
    "content": "NoNonsense Notes v7.2.3\n\nÖnemli noktalar:\n- https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md adresindeki eğitim tamamlandı\n- Yeni Android sürümleri için kısayol widget'ı düzeltildi\n- Not listesi widget'ıyla ilgili hatalar düzeltildi\n- Android 16 için resmi destek eklendi\n- \"Arama sonuçları\" ve \"arşivlenen notlar\" görünümlerinde tarihlerin gösterilme şekli iyileştirildi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/changelogs/72600.txt",
    "content": "NoNonsense Notes v7.2.6\n\nÖnemli noktalar:\n- https://github.com/spacecowboy/NotePad/blob/master/documents/TUTORIAL.md adresindeki öğretici tamamlandı\n- Yeni Android sürümleri için kısayol widget'ı düzeltildi\n- Not listesi widget'ıyla ilgili hatalar düzeltildi\n- Android 16 için resmi destek eklendi\n- “Arama sonuçları” ve \"arşivlenen notlar\" görünümlerinde tarihlerin gösterilme şekli iyileştirildi\n- Uygulama derleme altyapısı güncellendi\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/full_description.txt",
    "content": "En son Android teknolojilerini kullanan açık kaynaklı not defteri ve yapılacaklar listesi uygulaması.\n\n* Dash saat desteği (evet, hala çalışıyor)\n* Bildirimler: haftanın belirli günlerinde isteğe bağlı tekrarlama ile belirli bir zamanda hatırlatıcılar ayarlayın\n* Dil seçimi\n* Kilit ekranı destekli, yeniden boyutlandırılabilir ve yapılandırılabilir widget\n* Önemli notları parola ile koruma\n* Görevleri farklı listeler arasında taşıma\n* Görevleri yeniden sıralamak için sürükleme\n* Android Ajanda widget'ı ile bütünleşme\n* Arama\n* Emojileri etiket olarak kullanma 🏷️\n* Açık ve koyu temalar\n* Notları bir JSON dosyasına yedekleme (içe ve dışa aktarma)\n* Yerel depolama alanındaki bir ORG dosyası ile eşitleme\n* Önceki tüm sürümlerle birlikte not geçmişi\n* Silinen notlar için geri dönüşüm kutusu\n\nBu uygulamayı geliştirmek için bağış yapın veya fikirlerinizi paylaşın,\nhttps://github.com/spacecowboy/NotePad adresini ziyaret edin\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/short_description.txt",
    "content": "Eşzamanlama desteği olan not defteri ve yapılacaklar uygulaması\n"
  },
  {
    "path": "fastlane/metadata/android/tr-TR/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/vec/full_description.txt",
    "content": "Stà applicasion la xè open source e adata ale pì recenti versioni del Android. La gà:\n\n* el reojo Dashclock\n* e notifiche pai promemoria, che i se ripete anca su pai dì dea setimana\n* tante lingue, anca quea veneta\n* i widget pal menù principae\n* a pasword par blocare e note importanti\n* note che te pui movare tra e liste\n* note che te pui trascinare sù e zo\n* integrasion co l'Android Agenda Widget\n* a bàra par sèrcare\n* i coeori a tema ciaro o scuro, a seconda dei to gusti\n* backup par salvarse i dati importanti\n* sincronisasion sua memoria del teefono cui file .org\n* se poe recuperar e versioni vecie dee note coa cronologia\n* cestino pae note canceae\n\nPàr migliorare sta app, o par contarme qualche to idea, vien trovarme su\nhttps://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/vec/short_description.txt",
    "content": "Nà applicasion semplice par segnàrse e robe da fare\n"
  },
  {
    "path": "fastlane/metadata/android/vec/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/70000.txt",
    "content": "v7.0.0: NoNonsense Notes\n\n重点：\n- 兼容 Android 12\n- 去除 Google Tasks 同步\n- 可以将 ORG 和 JSON 文件保存在几个选定的地方。你可以选择保存在哪个地方。\n\n详情：\n- 提高 target SDK 到 version 32\n- 升级 AndroidX 库\n- 修复所有 tests\n- 升级依赖项和 Gradle\n- 常规代码清理\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/full_description.txt",
    "content": "使用最新 Android 技术的开源笔记和待办事项应用\n\n* Dash clock 支持（是的，它仍能正常工作）\n* 通知：在特定时间设置提醒，并在特定工作日重复（可选）\n* 选择语言\n* 可调整大小可配置的小部件，支持锁屏显示\n* 用密码保护重要笔记\n* 可以将任务在不同列表间移动\n* 拖动重新排列任务顺序\n* 与 Android Agenda 小部件集成\n* 搜索\n* 将 emoji 用作标签🏷️\n* 浅色和深色主题\n* 备份（导入和导出） 笔记到 JSON 文件\n* 和本地存储的 ORG 文件同步\n* 记录所有先前版本的笔记历史\n* 已删除笔记的回收站\n\n要帮忙改进应用，请捐款或者简单地分享所思所想\n请访问 https://github.com/spacecowboy/NotePad\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/short_description.txt",
    "content": "支持同步的记事本和待办事项应用\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/title.txt",
    "content": "NoNonsense Notes\n"
  },
  {
    "path": "github_on_emu_started.sh",
    "content": "#!/bin/bash\n\n# once the android emulator starts, this script tries to:\n# * close all popups\n# * configure the emulator\n# * start recording a video\n# * run the tests\n#\n# sources:  https://stackoverflow.com/a/66155744/6307322\n#           https://stackoverflow.com/a/62723329/6307322\n# it will run all commands, even if one fails\n\necho \"github action script started\"\n\n# wait a bit for the emulator to finish loading\nsleep 15\n\n# take screenshot of the host PC after the emulator starts\nscreencapture screenshot-desktop.jpg\n\n# output may be useful\nadb devices\n\n# try to close all OS popups in the emulator, like \"System UI stopped working\"\nadb shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS\n\n# close that useless popup that tells you how to exit fullscreen mode\nadb shell settings put secure immersive_mode_confirmations confirmed\n\n# manually uninstall the app, if possible. Sometimes it's needed\nadb shell pm uninstall --user 0 com.nononsenseapps.notepad.test\nadb shell pm uninstall --user 0 com.nononsenseapps.notepad\n\n# if the host PC is under heavy load, the emulator sometimes receives a \"long click\" instead\n# of a normal \"click\" signal, thus some tests will fail. The workaround is to raise the\n# treshold for a \"long click\" from ~400ms to 3s, so it will be harder to send long clicks\n# by mistake\nadb shell settings put secure long_press_timeout 3000\n\n# disable animations once again, for safety\nadb shell settings put global window_animation_scale 0\nadb shell settings put global transition_animation_scale 0\nadb shell settings put global animator_duration_scale 0\n\nfunction getScreenStreamFromEmu() {\n\twhile true; do\n\t# exec-out: run command on emulated android, get the output on the host PC\n\t# bitrate & size: to get a smaller video\n\t# output-format and - at the end: stream to console output, don't save\n\t# bugreport: helpful info when a video starts\n\t# alternative: `adb emu screenrecord start --bit-rate 100000 --size 540x960 ./vid.webm`,\n\t#   but the tool is hardcoded to die after 3 minutes. Thanks Google.\n\t# returns: 0 if 3 minutes passed, a number > 0 if there was an error\n\tadb exec-out screenrecord --output-format=h264 --bit-rate 1M --size 480x854 --bugreport -\n\tADB_RET_CODE=$?\n  if [ $ADB_RET_CODE -gt 0 ] ; then\n    # the emulator died: exit the loop & the function. The video is saved correctly\n    echo \"[getScreenStreamFromEmu] Exiting...\" >&2\n    break\n  fi\n  # should go to STDERR, without polluting the video stream, hopefully\n  echo \"[getScreenStreamFromEmu] restarting screenrecord, PID = \" $! \". \" $ADB_RET_CODE \" was the return code\" >&2\n\tdone\n}\n\n# save the video stream to a file\n# { getScreenStreamFromEmu | ffmpeg -i - -s 480x854 -loglevel error \\\n# -nostats -hide_banner -framerate 15 -bufsize 1M emu-video.mp4 ; } &\n\n# press \"HOME\" to close any \"system UI not responding\" popups still showing.\n# Notice that these popups appear when the host PC is too slow, so the best solution\n# is to run the tests under a different host OS, device skin, API version, ...\nadb shell input keyevent 3\n\n# clear logcat before tests begin\nadb logcat -c\n\n# run tests\n./gradlew connectedCheck\nGRADLE_RETURN_CODE=$?\n\n# dump the logcat to a file. Log level: Debug\nadb logcat -d '*:D' > logcat-dump.txt\n\n# check if pictures, videos & logs exist\necho \"----------\"\nls -lh -- *.mp4 *.png *.jpg logcat-dump.txt\necho \"----------\"\n\n# return with the code from gradle, so the github action step can fail if the tests failed\nexit $GRADLE_RETURN_CODE\n\n# then the github action will stop the emulator, and getScreenStreamFromEmu() will stop\n# the recording, we don't have to do it manually.\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.3-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.caching=true\norg.gradle.daemon=true\n\n# Gradle will run in incubating parallel mode\norg.gradle.parallel=true\n\norg.gradle.configureondemand=true\nandroid.useAndroidX=true\nandroid.enableJetifier=false\n\n# Enables namespacing of each library's R class so that its R class includes\n# only the resources declared in the library itself and none from the library's\n# dependencies, thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true\n\n# Specifies the JVM arguments used for the daemon process,\n# for tweaking memory settings\norg.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g\n\n# show deprecated gradle features used in scripts\norg.gradle.warning.mode=all\n\n# until we remove AndroidAnnotations, classes like ActivityMain still need\n# @EActivity(R.layout.activity_main), therefore resource Ids must be final\nandroid.nonFinalResIds=false\n\n# does not support custom tasks that read files, like :checkLanguages and :checkFastlane\norg.gradle.configuration-cache=false\n\n# always leave the last row empty or the play store deployment won't work!\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "release.sh",
    "content": "#!/bin/bash\n\n# Run checks and prepare a release build\n#\n# Credits to Feeder by @spacecowboy\n# https://gitlab.com/spacecowboy/Feeder/\n\nset -u\n\nTARGET=\"${1:-HEAD}\"\n\ncurrent_default=\"$(git describe --tags --abbrev=0 \"${TARGET}\")\"\n\necho >&2 -n \"Current version is ${current_default}, \"\n# read -r current_in\n\nnext_default=\"$(grep \"versionName \" ./app/build.gradle | sed \"s|\\s*versionName \\\"\\(.*\\)\\\"|\\\\1|\")\"\necho >&2 -n \"next version [press 'enter' for ${next_default}]: \"\nread -r next_in\n\nif [ -z \"${next_in}\" ]; then\n  NEXT_VERSION=\"${next_default}\"\nelse\n  NEXT_VERSION=\"${next_in}\"\nfi\n\nCURRENT_CODE=\"$(grep \"versionCode\" ./app/build.gradle | sed \"s|\\s*versionCode \\([0-9]\\+\\)|\\\\1|\")\"\necho >&2 \"Current code ${CURRENT_CODE}\"\n\n# The old version was 57130, so we have to use the thousands for the minor code, like 70100.\n# The last 2 digits are useless, but it can't be helped\nnext_code_default=$(( CURRENT_CODE + 100 ))\n\necho >&2 -n \"Next code [press 'enter' to confirm ${next_code_default}]: \"\nread -r next_code_in\n\nif [ -z \"${next_code_in}\" ]; then\n  NEXT_CODE=\"${next_code_default}\"\nelse\n  NEXT_CODE=\"${next_code_in}\"\nfi\n\nread -r -p \"Check consistency of languages list with values- folders? [y/N] \" response\nif [[ \"$response\" =~ ^[yY]$ ]]\nthen\n  ./gradlew checkLanguages checkFastlane --quiet\nfi\n\n# empty new line\necho >&2 \"\"\necho >&2 \"Opening changelog editor...\"\necho >&2 \"\"\n\n# changelog template. To add all the new commit messages, move this line inside the \"\"\n\nCL=\"NoNonsense Notes v${NEXT_VERSION}\n\nHighlights:\n- FILL AND SAVE THIS FILE\n\nDetails:\n-\nWrite no more than 500 words. Include the following:\n$(git shortlog -w76,2,9 --format='* %s' \"${current_default}\"..HEAD)\n\"\n\ntmpfile=\"$(mktemp)\"\n\necho \"${CL}\" > \"${tmpfile}\"\n\nif hash notepad 2>/dev/null; then\n  # edit with notepad\n  notepad \"${tmpfile}\"\nelse\n  # fallback\n  nano \"${tmpfile}\"\nfi\n\necho >&2 \"\"\necho >&2 \"Changelog for [${NEXT_VERSION}]:\"\ncat >&2 \"${tmpfile}\"\necho >&2\n\nread -r -p \"Write changelog? [y/N] \" response\nif [[ \"$response\" =~ ^[yY]$ ]]\nthen\n  # Playstore has a limit\n  head --bytes=500 \"${tmpfile}\" >\"fastlane/metadata/android/en-US/changelogs/${NEXT_CODE}.txt\"\nfi\n\n# update versions on build.gradle\nread -r -p \"Update gradle versions? [y/N] \" response\nif [[ \"$response\" =~ ^[yY]$ ]]\nthen\n  sed -i \"s|\\(\\s*versionCode \\)[0-9]\\+|\\\\1${NEXT_CODE}|\" app/build.gradle\n  sed -i \"s|\\(\\s*versionName \\).*|\\\\1\\\"${NEXT_VERSION}\\\"|\" app/build.gradle\nfi\n\nread -r -p \"Commit changes? [y/N] \" response\nif [[ \"$response\" =~ ^[yY]$ ]]\nthen\n  git add \"fastlane/metadata/android/en-US/changelogs/${NEXT_CODE}.txt\"\n  git add app/build.gradle\n  git commit -m \"Releasing ${NEXT_VERSION} from release.sh\"\nfi\n\nread -r -p \"Make tag? [y/N] \" response\nif [[ \"$response\" =~ ^[yY]$ ]]\nthen\n  git tag -am \"$(cat \"${tmpfile}\")\" \"${NEXT_VERSION}\"\nfi\n\necho \"Status of git files:\"\ngit status -s\n\nread -r -p \"Push files and tags to remote? [y/N] \" response\nif [[ \"$response\" =~ ^[yY]$ ]]\nthen\n  git push\n  git push --tags\nfi\n\n# github page to create releases\nWEBPAGE=https://github.com/spacecowboy/NotePad/releases/new\necho $WEBPAGE\n\nread -r -p \"Open that github page to make a new release? [y/N] \" response\nif [[ \"$response\" =~ ^[yY]$ ]]\nthen\n  if hash start 2>/dev/null; then\n    # open URL on windows\n    start $WEBPAGE\n  else\n    # open URL on linux\n    xdg-open $WEBPAGE\n  fi\nfi\n"
  },
  {
    "path": "settings.gradle",
    "content": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\ninclude ':app',\n        ':contract',\n        ':external:drag-sort-listview'\n"
  }
]