Full Code of spacecowboy/NotePad for AI

master 3b18a5bd2510 cached
527 files
1.8 MB
517.7k tokens
1574 symbols
1 requests
Download .txt
Showing preview only (2,107K chars total). Download the full file or copy to clipboard to get everything.
Repository: spacecowboy/NotePad
Branch: master
Commit: 3b18a5bd2510
Files: 527
Total size: 1.8 MB

Directory structure:
gitextract_hq_i4sxh/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── notifications_do_not_appear.md
│   └── workflows/
│       ├── android_build.yml
│       ├── android_tests.yml
│       └── publish_playstore.yml
├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── build.gradle
│   ├── dbgenout/
│   │   └── com/
│   │       └── nononsenseapps/
│   │           └── notepad/
│   │               └── database/
│   │                   ├── DBItem.java
│   │                   ├── DatabaseHandler.java
│   │                   ├── DatabaseTriggers.java
│   │                   ├── DatabaseViews.java
│   │                   ├── ItemProvider.java
│   │                   └── taskviewItem.java
│   ├── dbsetup.py
│   ├── proguard.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── nononsenseapps/
│       │               └── notepad/
│       │                   ├── espresso_tests/
│       │                   │   ├── BaseTestClass.java
│       │                   │   ├── EspressoHelper.java
│       │                   │   ├── TestAddBigNumberOfNotesScrollDownAndDeleteOne.java
│       │                   │   ├── TestAddNewNoteShouldShowNameInNotesScreen.java
│       │                   │   ├── TestAddNewNoteWithDueDateCheckDateIsVisible.java
│       │                   │   ├── TestAddNewNoteWithReminderDateAndTime.java
│       │                   │   ├── TestAddNoteToTaskList.java
│       │                   │   ├── TestAddNotesAndRotateScreen.java
│       │                   │   ├── TestAddTaskListAndRotateScreen.java
│       │                   │   ├── TestAddTaskListCheckItIsAddedToDrawer.java
│       │                   │   ├── TestAddTaskListsScrollNavigationDrawer.java
│       │                   │   ├── TestCompletedTasksAreCleared.java
│       │                   │   ├── TestCreateNoteAndDeleteIt.java
│       │                   │   ├── TestCreateTaskListAndDeleteIt.java
│       │                   │   ├── TestPasswords.java
│       │                   │   └── TestSaveLoadJsonBackup.java
│       │                   └── test/
│       │                       ├── DBFreshTest.java
│       │                       ├── DBProviderMovementTest.java
│       │                       ├── DBProviderTest.java
│       │                       ├── DBUpgradeTest.java
│       │                       ├── DaoTaskTest.java
│       │                       ├── DashClockSettingsTest.java
│       │                       ├── DateTimeTest.java
│       │                       ├── FragmentTaskDetailTest.java
│       │                       ├── FragmentTaskListsTest.java
│       │                       ├── FragmentTaskListsViewPagerTest.java
│       │                       ├── Helper.java
│       │                       ├── OrgSyncTest.java
│       │                       ├── ProviderHelperTest.java
│       │                       ├── RFCDateTest.java
│       │                       └── StorageTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── .gitignore
│           ├── java/
│           │   └── com/
│           │       ├── google/
│           │       │   └── android/
│           │       │       └── apps/
│           │       │           └── dashclock/
│           │       │               └── ui/
│           │       │                   └── DragGripView.java
│           │       └── nononsenseapps/
│           │           ├── helpers/
│           │           │   ├── ActivityHelper.java
│           │           │   ├── DocumentFileHelper.java
│           │           │   ├── FileHelper.java
│           │           │   ├── FilePickerHelper.java
│           │           │   ├── ListHelper.java
│           │           │   ├── NnnLogger.java
│           │           │   ├── NotificationHelper.java
│           │           │   ├── PermissionsHelper.java
│           │           │   ├── PreferencesHelper.java
│           │           │   ├── RFC3339Date.java
│           │           │   ├── SyncStatusMonitor.java
│           │           │   ├── ThemeHelper.java
│           │           │   ├── TimeFormatter.java
│           │           │   └── UpdateNotifier.java
│           │           ├── notepad/
│           │           │   ├── NnnApp.java
│           │           │   ├── NotePadBroadcastReceiver.java
│           │           │   ├── activities/
│           │           │   │   ├── ActivitySearch.java
│           │           │   │   ├── ActivitySearchDeleted.java
│           │           │   │   ├── ActivityTaskHistory.java
│           │           │   │   └── main/
│           │           │   │       ├── ActivityMain.java
│           │           │   │       ├── ActivityMainHelper.java
│           │           │   │       └── DrawerCursorLoader.java
│           │           │   ├── android/
│           │           │   │   └── provider/
│           │           │   │       ├── DummyProvider.java
│           │           │   │       ├── ProviderHelper.java
│           │           │   │       ├── ProviderManager.java
│           │           │   │       └── TextFileProvider.java
│           │           │   ├── dashclock/
│           │           │   │   ├── DashclockPrefActivity.java
│           │           │   │   ├── DashclockPrefsFragment.java
│           │           │   │   └── TasksExtension.java
│           │           │   ├── database/
│           │           │   │   ├── DAO.java
│           │           │   │   ├── DatabaseHandler.java
│           │           │   │   ├── LegacyDBHelper.java
│           │           │   │   ├── MyContentProvider.java
│           │           │   │   ├── Notification.java
│           │           │   │   ├── RemoteTask.java
│           │           │   │   ├── RemoteTaskList.java
│           │           │   │   ├── Task.java
│           │           │   │   └── TaskList.java
│           │           │   ├── fragments/
│           │           │   │   ├── DialogConfirmBase.java
│           │           │   │   ├── DialogConfirmBaseV11.java
│           │           │   │   ├── DialogDeleteCompletedTasks.java
│           │           │   │   ├── DialogDeleteList.java
│           │           │   │   ├── DialogDeleteTask.java
│           │           │   │   ├── DialogEditList.java
│           │           │   │   ├── DialogExportBackup.java
│           │           │   │   ├── DialogMoveToList.java
│           │           │   │   ├── DialogPassword.java
│           │           │   │   ├── DialogPasswordV11.java
│           │           │   │   ├── DialogRestore.java
│           │           │   │   ├── DialogRestoreBackup.java
│           │           │   │   ├── FragmentSearch.java
│           │           │   │   ├── FragmentSearchDeleted.java
│           │           │   │   ├── TaskDetailFragment.java
│           │           │   │   ├── TaskListFragment.java
│           │           │   │   └── TaskListViewPagerFragment.java
│           │           │   ├── interfaces/
│           │           │   │   ├── ListOpener.java
│           │           │   │   ├── MenuStateController.java
│           │           │   │   └── OnFragmentInteractionListener.java
│           │           │   ├── prefs/
│           │           │   │   ├── AboutPrefs.java
│           │           │   │   ├── AppearancePrefs.java
│           │           │   │   ├── BackupPrefs.java
│           │           │   │   ├── Constants.java
│           │           │   │   ├── IndexPrefs.java
│           │           │   │   ├── ListPrefs.java
│           │           │   │   ├── NotificationPrefs.java
│           │           │   │   ├── PasswordPrefs.java
│           │           │   │   ├── PrefsActivity.java
│           │           │   │   └── SyncPrefs.java
│           │           │   ├── sync/
│           │           │   │   ├── SyncAdapter.java
│           │           │   │   ├── files/
│           │           │   │   │   └── JSONBackup.java
│           │           │   │   ├── googleapi/
│           │           │   │   │   ├── GoogleTask.java
│           │           │   │   │   └── GoogleTaskList.java
│           │           │   │   └── orgsync/
│           │           │   │       ├── BackgroundSyncScheduler.java
│           │           │   │       ├── DBSyncBase.java
│           │           │   │       ├── Monitor.java
│           │           │   │       ├── OrgConverter.java
│           │           │   │       ├── OrgProvider.java
│           │           │   │       ├── OrgSyncService.java
│           │           │   │       ├── RemoteTaskListFile.java
│           │           │   │       ├── RemoteTaskNode.java
│           │           │   │       ├── SDSynchronizer.java
│           │           │   │       ├── Synchronizer.java
│           │           │   │       └── SynchronizerInterface.java
│           │           │   └── widget/
│           │           │       ├── list/
│           │           │       │   ├── ListWidgetConfig.java
│           │           │       │   ├── ListWidgetProvider.java
│           │           │       │   ├── ListWidgetService.java
│           │           │       │   └── WidgetPrefs.java
│           │           │       └── shortcut/
│           │           │           └── ShortcutConfig.java
│           │           └── ui/
│           │               ├── DateView.java
│           │               ├── DelegateFrame.java
│           │               ├── ExtraTypesCursorAdapter.java
│           │               ├── ExtrasCursorAdapter.java
│           │               ├── GreyableToggleButton.java
│           │               ├── NoteCheckBox.java
│           │               ├── NotificationItemHelper.java
│           │               ├── ShowcaseHelper.java
│           │               ├── StyledEditText.java
│           │               ├── TitleNoteTextView.java
│           │               ├── ViewsHelper.java
│           │               └── WeekDaysView.java
│           └── res/
│               ├── anim/
│               │   ├── activity_slide_in_right.xml
│               │   ├── activity_slide_out_right_full.xml
│               │   ├── cycle_7.xml
│               │   ├── shake.xml
│               │   ├── slide_in_bottom.xml
│               │   ├── slide_in_top.xml
│               │   ├── slide_out_bottom.xml
│               │   └── slide_out_top.xml
│               ├── drawable/
│               │   ├── btn_toggle_bg.xml
│               │   ├── folder_move_24dp.xml
│               │   ├── folder_plus_24dp.xml
│               │   ├── ic_add_24dp.xml
│               │   ├── ic_alarm_24dp.xml
│               │   ├── ic_alarm_add_24dp.xml
│               │   ├── ic_archive_24dp.xml
│               │   ├── ic_brightness_6_24dp.xml
│               │   ├── ic_check_24dp.xml
│               │   ├── ic_checkbox_checked.xml
│               │   ├── ic_checkbox_unchecked.xml
│               │   ├── ic_clear_24dp.xml
│               │   ├── ic_copy_24dp.xml
│               │   ├── ic_delete_24dp.xml
│               │   ├── ic_export.xml
│               │   ├── ic_folder_24dp.xml
│               │   ├── ic_help_24dp.xml
│               │   ├── ic_import.xml
│               │   ├── ic_info_24dp.xml
│               │   ├── ic_lock_closed_24dp.xml
│               │   ├── ic_lock_open_24dp.xml
│               │   ├── ic_notebook_minus_24dp.xml
│               │   ├── ic_refresh_24dp.xml
│               │   ├── ic_sd_storage_24dp.xml
│               │   ├── ic_search_24dp.xml
│               │   ├── ic_select_all.xml
│               │   ├── ic_settings_24dp.xml
│               │   ├── ic_share_24dp.xml
│               │   ├── ic_sort_24dp.xml
│               │   ├── ic_stat_notification_edit.xml
│               │   ├── ic_underline_accent.xml
│               │   ├── ic_undo_24dp.xml
│               │   ├── img_default_selector_dark.xml
│               │   ├── img_default_selector_light.xml
│               │   ├── tasklist_item_blackclassic_bg.xml
│               │   ├── tasklist_item_darkcard_bg.xml
│               │   ├── tasklist_item_lightcard_bg.xml
│               │   └── tasklist_item_lightclassic_bg.xml
│               ├── drawable-anydpi-v26/
│               │   ├── app_icon.xml
│               │   ├── icon_foreground_symbol.xml
│               │   ├── icon_gradient.xml
│               │   └── icon_monochrome.xml
│               ├── layout/
│               │   ├── actionbar_custom_view_done.xml
│               │   ├── actionbar_custom_view_done_discard.xml
│               │   ├── actionbar_discard_button.xml
│               │   ├── actionbar_done_button.xml
│               │   ├── activity_dashclock_settings.xml
│               │   ├── activity_main.xml
│               │   ├── activity_settings.xml
│               │   ├── activity_shortcut_config.xml
│               │   ├── activity_task_history.xml
│               │   ├── activity_widget_config.xml
│               │   ├── activity_widget_config_part_preview.xml
│               │   ├── activity_widget_config_part_settings.xml
│               │   ├── app_pref_about_layout.xml
│               │   ├── app_pref_password_layout.xml
│               │   ├── dialog_ok_cancel.xml
│               │   ├── drawer_header.xml
│               │   ├── drawer_layout.xml
│               │   ├── fragment_dialog_editlist.xml
│               │   ├── fragment_dialog_movetolist.xml
│               │   ├── fragment_dialog_password.xml
│               │   ├── fragment_dialog_restore.xml
│               │   ├── fragment_search.xml
│               │   ├── fragment_task_detail.xml
│               │   ├── fragment_task_list.xml
│               │   ├── fragment_tasklist_viewpager.xml
│               │   ├── fullscreen_fragment.xml
│               │   ├── notification_view.xml
│               │   ├── simple_light_list_item_activated_1.xml
│               │   ├── simple_listitem.xml
│               │   ├── spinner_item.xml
│               │   ├── tasklist_header.xml
│               │   ├── tasklist_item_card_section.xml
│               │   ├── tasklist_item_rich.xml
│               │   ├── weekdays_layout.xml
│               │   ├── widget_layout.xml
│               │   ├── widgetlist_header.xml
│               │   └── widgetlist_item.xml
│               ├── layout-sw600dp-land/
│               │   ├── activity_main.xml
│               │   └── activity_settings.xml
│               ├── menu/
│               │   ├── activity_deleted_context.xml
│               │   ├── activity_main.xml
│               │   ├── fragment_search.xml
│               │   ├── fragment_tasklist.xml
│               │   ├── fragment_tasklist_context.xml
│               │   ├── fragment_tasklists_viewpager.xml
│               │   └── fragment_tasks_detail.xml
│               ├── values/
│               │   ├── arrays.xml
│               │   ├── attr.xml
│               │   ├── bool.xml
│               │   ├── color.xml
│               │   ├── constants.xml
│               │   ├── dashclock_preference_values.xml
│               │   ├── dimens.xml
│               │   ├── layout_constants.xml
│               │   ├── strings.xml
│               │   ├── styles.xml
│               │   ├── themes.xml
│               │   └── widget_params.xml
│               ├── values-af/
│               │   └── strings.xml
│               ├── values-ar/
│               │   └── strings.xml
│               ├── values-bg/
│               │   └── strings.xml
│               ├── values-ca-rES/
│               │   └── strings.xml
│               ├── values-co/
│               │   └── strings.xml
│               ├── values-cs/
│               │   └── strings.xml
│               ├── values-da/
│               │   └── strings.xml
│               ├── values-de/
│               │   └── strings.xml
│               ├── values-el/
│               │   └── strings.xml
│               ├── values-es/
│               │   └── strings.xml
│               ├── values-et/
│               │   └── strings.xml
│               ├── values-fa/
│               │   └── strings.xml
│               ├── values-fi-rFI/
│               │   └── strings.xml
│               ├── values-fr/
│               │   └── strings.xml
│               ├── values-gl/
│               │   └── strings.xml
│               ├── values-hu/
│               │   └── strings.xml
│               ├── values-in-rID/
│               │   └── strings.xml
│               ├── values-is/
│               │   └── strings.xml
│               ├── values-it/
│               │   └── strings.xml
│               ├── values-iw-rIL/
│               │   └── strings.xml
│               ├── values-ja/
│               │   └── strings.xml
│               ├── values-ko/
│               │   └── strings.xml
│               ├── values-land/
│               │   ├── constants.xml
│               │   └── dimens.xml
│               ├── values-nb-rNO/
│               │   └── strings.xml
│               ├── values-nl/
│               │   └── strings.xml
│               ├── values-pl/
│               │   └── strings.xml
│               ├── values-pt/
│               │   └── strings.xml
│               ├── values-pt-rBR/
│               │   └── strings.xml
│               ├── values-pt-rPT/
│               │   └── strings.xml
│               ├── values-ro/
│               │   └── strings.xml
│               ├── values-ru/
│               │   └── strings.xml
│               ├── values-sk/
│               │   └── strings.xml
│               ├── values-sl/
│               │   └── strings.xml
│               ├── values-sv/
│               │   └── strings.xml
│               ├── values-sw600dp/
│               │   ├── bool.xml
│               │   └── dimens.xml
│               ├── values-sw600dp-land/
│               │   ├── dimens.xml
│               │   └── layout_constants.xml
│               ├── values-sw720dp/
│               │   └── dimens.xml
│               ├── values-sw720dp-land/
│               │   ├── dimens.xml
│               │   └── layout_constants.xml
│               ├── values-ta/
│               │   └── strings.xml
│               ├── values-tr/
│               │   └── strings.xml
│               ├── values-uk/
│               │   └── strings.xml
│               ├── values-v26/
│               │   └── styles.xml
│               ├── values-vec/
│               │   └── strings.xml
│               ├── values-vi/
│               │   └── strings.xml
│               ├── values-zh-rCN/
│               │   └── strings.xml
│               ├── values-zh-rTW/
│               │   └── strings.xml
│               └── xml/
│                   ├── app_pref_backup.xml
│                   ├── app_pref_headers.xml
│                   ├── app_pref_list.xml
│                   ├── app_pref_main.xml
│                   ├── app_pref_notifications.xml
│                   ├── app_pref_sync.xml
│                   ├── dashclock_pref_general.xml
│                   ├── listwidgetinfo.xml
│                   ├── searchable.xml
│                   └── searchabledeleted.xml
├── build.gradle
├── contract/
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── java/
│               └── com/
│                   └── nononsenseapps/
│                       └── notepad/
│                           └── providercontract/
│                               ├── ProviderBase.java
│                               ├── ProviderContract.java
│                               └── UriContract.java
├── customTasks.gradle
├── deploy_playstore.sh
├── documents/
│   ├── CONTRIBUTING.md
│   ├── FAQ.md
│   ├── PRIVACY.md
│   └── TUTORIAL.md
├── external/
│   └── drag-sort-listview/
│       ├── AndroidManifest.xml
│       ├── build.gradle
│       ├── res/
│       │   └── values/
│       │       └── dslv_attrs.xml
│       └── src/
│           └── com/
│               └── mobeta/
│                   └── android/
│                       └── dslv/
│                           ├── DragSortController.java
│                           ├── DragSortCursorAdapter.java
│                           ├── DragSortItemView.java
│                           ├── DragSortItemViewCheckable.java
│                           ├── DragSortListView.java
│                           ├── ResourceDragSortCursorAdapter.java
│                           ├── SimpleDragSortCursorAdapter.java
│                           └── SimpleFloatViewManager.java
├── fastlane/
│   ├── Appfile
│   ├── Fastfile
│   └── metadata/
│       └── android/
│           ├── ar/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── bg/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   └── 71900.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── cs-CZ/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── de-DE/
│           │   ├── changelogs/
│           │   │   └── 57130.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── en-US/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   ├── 72000.txt
│           │   │   ├── 72100.txt
│           │   │   ├── 72200.txt
│           │   │   ├── 72300.txt
│           │   │   └── 72600.txt
│           │   ├── full_description.txt
│           │   ├── images/
│           │   │   ├── featureGraphic.psd
│           │   │   ├── featureGraphic.xcf
│           │   │   └── promoGraphic.psd
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── es-ES/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   └── 71900.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── fi-FI/
│           │   ├── changelogs/
│           │   │   └── 71400.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── fr-FR/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── he/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── id/
│           │   ├── changelogs/
│           │   │   └── 57130.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── is/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── it-IT/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── nb-NO/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   └── 71000.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── pl-PL/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   └── 71700.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── pt-BR/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── pt-PT/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── ro/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── ru-RU/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   ├── 72000.txt
│           │   │   ├── 72100.txt
│           │   │   ├── 72200.txt
│           │   │   ├── 72300.txt
│           │   │   └── 72600.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── ta-IN/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   └── 72000.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── tr-TR/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   ├── 72000.txt
│           │   │   ├── 72100.txt
│           │   │   ├── 72200.txt
│           │   │   ├── 72300.txt
│           │   │   └── 72600.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── vec/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           └── zh-CN/
│               ├── changelogs/
│               │   └── 70000.txt
│               ├── full_description.txt
│               ├── short_description.txt
│               └── title.txt
├── github_on_emu_started.sh
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── release.sh
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# multiple github accounts are allowed, but only one of everything else. Setup is explained in:
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository
github: [spacecowboy, CampelloManuel]
ko_fi: spacecowboy
custom: https://www.paypal.com/paypalme/campellomanuel


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Report issues of the app's features
title: ''
labels: bug
assignees: ''

---

### Describe the bug

write a clear and short description of what the bug is.

### how To Reproduce

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

also state what is the expected result and what is the actual result.

### If applicable

* add the stacktrace
* add logcat output as explained [here](https://f-droid.org/docs/Getting_logcat_messages_after_crash/)

### Technical information

- Device: [e.g. Nexus 7 (2013)]
- OS: [e.g. LineageOS 15.1]
- app version: [e.g. v3.2.4]
- app commit id (only for nightly builds): [e.g. 4b333bb]

If relevant, please say what your sync settings are, and report any odd stuff happening in general.



================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for the app
title: ''
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here


================================================
FILE: .github/ISSUE_TEMPLATE/notifications_do_not_appear.md
================================================
---
name: Notifications don't show up
about: The app does not show notifications for reminders
title: ''
labels: bug
assignees: ''

---

**Please describe the problem and how to reproduce it:**

...

**Checklist**

- [ ] Consulted [https://dontkillmyapp.com/]
- [ ] Power management: battery optimization is disabled for OpenTracks

**Technical information**

- Device: [e.g. Nexus 7 (2013)]
- OS: [e.g. LineageOS 15.1]
- app version: [e.g. v3.2.4]
- app commit id (only for nightly builds): [e.g. 4b333bb]

================================================
FILE: .github/workflows/android_build.yml
================================================
name: Android build

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:
  # this job builds and uploads the apk
  build_the_apk:
    # there's no need to run it on forks
    if: github.repository == 'spacecowboy/NotePad'
    runs-on: ubuntu-latest    
    permissions:
      contents: read    
    steps:
    - name: perform the checkout
      uses: actions/checkout@v4
      
    - name: perform the validation
      uses: gradle/actions/wrapper-validation@v3
      
    - name: perform the JDK setup
      uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'temurin'
        cache: 'gradle'

    - name: perform Gradle caching
      uses: gradle/actions/setup-gradle@v3

    - name: perform the Gradle build
      run: ./gradlew build

    - name: perform additional checks with our custom tasks
      run: ./gradlew checkLanguages checkFastlane

    # TODO output files from this step are not saved. Take a look at them
    - name: perform lint checks with gradle
      run: ./gradlew lint

    - name: perform the APK upload
      uses: actions/upload-artifact@v4
      with:
        name: app_debug
        path: app/build/outputs/apk/debug/app-debug.apk
        retention-days: 7 # we're not publishing the app: nobody needs this apk


================================================
FILE: .github/workflows/android_tests.yml
================================================
name: Android tests

on:
  push:
    branches: [ "master" ]
    # runs when you commit to the master branch and when merging pull requests.
    # Not on receiving pull requests, as it would be a waste.
    paths-ignore:
      - '**.md'
      - '.github/**'
      - '!.github/workflows/build.yml'
jobs:
  # this job runs all the tests
  job_tests:
    if: github.repository == 'spacecowboy/NotePad' # no need to run it on forks    
    timeout-minutes: 45 # test jobs take ~11 minutes each. We give time to download the AVD images
    continue-on-error: true # run tests for ALL configurations, don't stop at the 1° failure
    strategy:
      matrix:
        # test on emulators with these Android API versions. They all have ATD images.
        api-level: [ 30, 32, 34, 35 ]
        # use stripped-down, test-friendly Android OS images without, or with, google apps
        target: [ aosp_atd, google_atd ]
        # profile: [ 5.1in WVGA, 10.1in WXGA ] # tests on tablets don't work anyway...
    runs-on: ubuntu-latest # List of installed software:
    # https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md
    permissions:
      contents: read
    steps:
      - name: perform the checkout
        uses: actions/checkout@v4

      - name: perform the set up for the JDK
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: temurin

      - name: perform the validation
        continue-on-error: true
        uses: gradle/actions/wrapper-validation@v3

      - name: perform Gradle setup & caching
        uses: gradle/actions/setup-gradle@v3

      - name: Check for hardware acceleration availability
        continue-on-error: true
        # Read the console logs for the result. The android emulator can't use
        # acceleration, according to the logs. No you can't install HAXM.
        run: $ANDROID_HOME/emulator/emulator -accel-check

      - name: Enable KVM group perms as required by reactivecircus/android-emulator-runner
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm  

      - name: perform AVD caching
        uses: actions/cache@v4
        id: avd-cache
        # they appear in
        # https://github.com/spacecowboy/NotePad/actions/caches?query=sort%3Asize-desc
        with:
          path: |
            ~/.android/avd/*
            ~/.android/adb*
          key: avd-${{ matrix.api-level }}-${{ matrix.target }}

      - name: create AVD and generate snapshot for caching
        # here it automatically installs the needed android SDK components
        # see https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md
        if: steps.avd-cache.outputs.cache-hit != 'true'
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: ${{ matrix.api-level }}
          target: ${{ matrix.target }}
          profile: 5.1in WVGA # low resolution: faster. This has no software buttons
          arch: x86_64
          disk-size: 2G       # needed for saving org and json files. We need 2 GB for API 34
          force-avd-creation: false
          # The emulator is picky on these parameters. For...
          # tests with "-accel on": see github run #84
          # tests with "-gpu host": the emulators never boot!
          # tests with "-gpu off": see github run #87, faster & more stable
          emulator-options: -no-boot-anim -no-window -gpu off
          disable-animations: true
          disable-spellchecker: true
          # starts the emulator, runs this script, then closes the emulator
          script: echo "Generated AVD snapshot for caching."

      - name: perform the Gradle build
        run: ./gradlew build

      - name: run custom tasks for additional checks
        run: ./gradlew checkLanguages checkFastlane

      - name: make the emulator script executable
        run: chmod +x github_on_emu_started.sh

      - name: run the tests
        # note that by now the app is already built
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: ${{ matrix.api-level }}
          target: ${{ matrix.target }}
          arch: x86_64
          profile: 5.4in FWVGA
          disk-size: 500M
          force-avd-creation: false
          disable-animations: true
          disable-spellchecker: true
          emulator-options: -no-snapshot-save -verbose -no-boot-anim -no-window -gpu off
          script: bash ./github_on_emu_started.sh

      - name: upload the generated files
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: files-api${{ matrix.api-level }}-${{ matrix.target }}
          path: |
            logcat-dump.txt
            app/build/reports/androidTests/**


================================================
FILE: .github/workflows/publish_playstore.yml
================================================
name: Publish to Play store

on:
  push:
    # Branch only during testing
    #branches:
    #  - master
    tags:
      - '*'

jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
    - name: perform the checkout
      uses: actions/checkout@v4
      with:
        submodules: 'recursive'
        fetch-depth: 0

    - name: perform the validation
      uses: gradle/actions/wrapper-validation@v3

    - name: perform the JDK setup
      uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'temurin'
        cache: 'gradle'

    - name: perform Gradle caching
      uses: gradle/actions/setup-gradle@v3

    - name: run custom tasks to ensure app can be published on play store
      run: ./gradlew checkLanguages checkFastlane

    - name: build and deploy
      run: ./deploy_playstore.sh
      env:
        SERVICEACCOUNTJSON: ${{ secrets.SERVICEACCOUNTJSON }}
        KEYSTOREPASSWORD: ${{ secrets.KEYSTOREPASSWORD }}
        KEYSTORE: ${{ secrets.KEYSTORE }}
        KEYPASSWORD: ${{ secrets.KEYPASSWORD }}
        KEYALIAS: ${{ secrets.KEYALIAS }}


================================================
FILE: .gitignore
================================================
secret.properties
_release_keys_
NotePad_ant
NotePad_ant/*
.factorypath
bin/
build/
.gradle
gen/
NotePad.apk
Notes_for_ICS_Market_Link.png
icon/
proguard/
.apt_generated
.settings*
translate-temp/
*~
*iml
.idea
local.properties


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.



================================================
FILE: README.md
================================================
# NoNonsense Notes

[![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/) \
<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"/> \
<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"/>

_A note-taking app for Android with reminders, since 2012_

[<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)

<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" />

## Translation

Help translate the app on [Hosted Weblate](https://hosted.weblate.org/projects/no-nonsense-notes/) \
<a href="https://hosted.weblate.org/engage/no-nonsense-notes/">
<img src="https://hosted.weblate.org/widgets/no-nonsense-notes/-/horizontal-auto.svg" alt="Translation status" />
</a>

## How does it work ?

Read the [tutorial](./documents/TUTORIAL.md) to learn about the app,
or the [FAQ](./documents/FAQ.md) for other kinds of questions.

## Reporting bugs

Please [report bugs](https://github.com/spacecowboy/NotePad/issues) using the provided template.

## Build the project

```sh
git clone https://github.com/spacecowboy/NotePad
cd NotePad
./gradlew check
./gradlew installDebug
```

if it does not work, [open an issue](https://github.com/spacecowboy/NotePad/issues)

## GPLv3+ License

<details>

```text
Copyright (C) 2014 Jonas Kalderstam

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
```

</details>

[Full license](./LICENSE)

## Useful links

* [FAQ](./documents/FAQ.md)
* [app tutorial](./documents/TUTORIAL.md)
* [Contribution guide](./documents/CONTRIBUTING.md)
* [Releases](https://github.com/spacecowboy/NotePad/releases)
* [Privacy policy](./documents/PRIVACY.md)
* [f-droid forum](https://forum.f-droid.org/t/i-want-to-update-an-old-discontinued-app-nononsense-notes/20037)


Built by @spacecowboy, maintained by @CampelloManuel. \
The app is currently being updated. Old versions are still available on F-droid.


================================================
FILE: app/build.gradle
================================================
/*
 * Copyright (c) 2015 Jonas Kalderstam.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

apply plugin: 'com.android.application'
// apply plugin: 'kotlin-android' we don't use kotlin for this app

android {
    compileSdk = 36
    namespace = "com.nononsenseapps.notepad" // for R.*

    buildFeatures {
        buildConfig = true
        // replaces part of the androidannotations library. See mBinding in java classes
        viewBinding = true
        // allow declaring different string resources for different buildTypes
        resValues = true
        // these features are useless for us
        compose = false
        aidl = false
        renderScript = false
        shaders = false
    }

    lint {
        htmlReport = false // TODO re-enable and fix all warnings reported in the html file
        textReport = false
        abortOnError = false
        explainIssues = true
        checkTestSources = true
        // turn off checking the given issue id's
        disable += ['RtlHardcoded', 'ApplySharedPref']
    }

    packagingOptions {
        resources {
            // excludes += ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt']
        }
    }

    if (project.hasProperty('STORE_FILE')) {
        signingConfigs {
            release {
                storeFile file(STORE_FILE)
                storePassword STORE_PASSWORD
                keyAlias KEY_ALIAS
                keyPassword KEY_PASSWORD
            }
        }
    } else {
        // println "No key store defined. Signed release not available..."
    }

    defaultConfig {
        applicationId "com.nononsenseapps.notepad"
        targetSdkVersion 36

        // most people have at least android 7.1 anyway: https://apilevels.com/
        // 93% of users are on API>=27 => don't write special code for 23<=API<=26, it's worthless
        minSdkVersion 23

        // If these values remain static, incremental builds are optimized and faster.
        // Also, FDROID is happier. Update them only with ./release.sh
        versionCode 72600
        versionName "7.2.6"

        vectorDrawables.useSupportLibrary = true

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        testInstrumentationRunnerArguments disableAnalytics: 'true'

        javaCompileOptions {
            annotationProcessorOptions {
                // you need these to make org.androidannotations:androidannotations compile
                // in "playStore" build type.
                // TODO get rid of androidannotations and delete this javaCompileOptions {...}
                arguments = [
                        "resourcePackageName": android.defaultConfig.applicationId,
                        "androidManifestFile": "$projectDir/src/main/AndroidManifest.xml".toString()
                ]
            }
        }
    }

    // disable code shrinking in debug, enable it in release mode
    buildTypes {
        debug {
            // identify the debug version to allow installing it
            // alongside the fdroid or play store version
            applicationIdSuffix ".debug"
            versionNameSuffix "-Debug"

            minifyEnabled = false
            shrinkResources = false
            pseudoLocalesEnabled = true
            // TODO test https://developer.android.com/guide/topics/resources/pseudolocales
            // just to make gradle shut up about missing classes
            proguardFiles 'proguard.pro'

            // to install the play store and fdroid versions simultaneously, content provider
            // authorities must be unique => define one for xml files
            resValue "string", "NnnAuthority_1", defaultConfig.applicationId + applicationIdSuffix + ".MyContentAuthority"
        }
        release {
            // the FDROID release, where we CAN'T use applicationIdSuffix or versionNameSuffix

            // enable R8
            minifyEnabled true
            shrinkResources = true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard.pro'
            if (project.hasProperty('STORE_FILE')) {
                signingConfig = signingConfigs.release
            }

            /// to install the play store and fdroid versions simultaneously, content provider
            // authorities must be unique => define one for xml and for java
            resValue "string", "NnnAuthority_1", defaultConfig.applicationId + ".MyContentAuthority"
        }
        playStore {
            // a RELEASE build for the google play store. The ONLY difference is the package name,
            // which lets you install this app alongside the version from f-droid
            applicationIdSuffix ".play"
            versionNameSuffix "-Play"
            // enable R8
            minifyEnabled true
            shrinkResources = true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard.pro'
            if (project.hasProperty('STORE_FILE')) {
                signingConfig = signingConfigs.release
            } else {
                println "STORE_FILE property is not set: cannot sign release build for the play store!"
            }
            // This is a sorted list of fallback build types used when a dependency does not
            // include a "playStore" build type. The plugin selects the first build type available
            // in the dependency. So this will use the "release" mode of drag-sort-listview for
            // our "playStore" profile
            matchingFallbacks = ['release', 'debug']

            // to install the play store and fdroid versions simultaneously, content provider
            // authorities must be unique => define one for xml and for java
            resValue "string", "NnnAuthority_1", defaultConfig.applicationId + applicationIdSuffix + ".MyContentAuthority"
        }
    }

    testOptions {
        // androidx espresso tests require this
        animationsDisabled = true
    }

    compileOptions {
        // enable support for new language APIs in old devices
        coreLibraryDesugaringEnabled = true
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }

    // kotlinOptions { jvmTarget = 17 } // compile kotlin to the same JAVA 17

    // control which types of configuration APKs you want your
    // app bundle to support
    bundle {
        language {
            // download ALL languages, because we have
            // a built-in language picker
            enableSplit = false
        }
    }
}

dependencies {
    // AndroidX
    implementation "androidx.cursoradapter:cursoradapter:1.0.0"
    implementation 'androidx.fragment:fragment:1.8.8'
    implementation 'androidx.appcompat:appcompat:1.7.1'
    implementation "androidx.preference:preference:1.2.1"
    implementation "androidx.startup:startup-runtime:1.2.0"
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    // the logcat complained once because we didn't have this
    implementation 'androidx.tracing:tracing:1.3.0'
    // pretty widgets
    implementation 'com.google.android.material:material:1.12.0'
    // Time library, open source & up to date
    implementation 'joda-time:joda-time:2.14.0'
    // Dashclock API, open source & abandoned, but it still works
    implementation 'com.google.android.apps.dashclock:dashclock-api:2.0.0'
    // Writes org files, open source & abandoned, but it still works
    implementation 'org.cowboyprogrammer.orgparser:orgparser:1.3.1'
    // for manual list sorting, local source copy, open source & abandoned
    implementation project(':external:drag-sort-listview')
    // for highlighting functionality on 1° launch, open source & up to date
    implementation 'com.getkeepsafe.taptargetview:taptargetview:1.15.0'
    // from deleted branch 'version6'
    implementation project(path: ':contract')
    // for ListenableFuture<>
    implementation 'com.google.guava:guava:33.4.8-android'
    // annotations library, open source & abandoned
    annotationProcessor "org.androidannotations:androidannotations:4.8.0"
    implementation "org.androidannotations:androidannotations-api:4.8.0"
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
    // Tests libraries
    androidTestImplementation 'androidx.test.ext:junit:1.3.0'
    androidTestImplementation 'androidx.test:rules:1.7.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
    androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.7.0'
    androidTestImplementation "androidx.test.espresso:espresso-intents:3.7.0"
    androidTestUtil "androidx.test.services:test-services:1.6.0"
    // no kotlin!
    // implementation "org.jetbrains.kotlin:kotlin-stdlib:1.7.20"
}


================================================
FILE: app/dbgenout/com/nononsenseapps/notepad/database/DBItem.java
================================================
package com.nononsenseapps.notepad.database;

import android.content.Context;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

public abstract class DBItem {
	public static final String COL_ID = "_id";

	public DBItem() {}

	public DBItem(final Cursor cursor) {}

	public abstract ContentValues getContent();

	public abstract String getTableName();

	public abstract long getId();

	public abstract void setId(final long id);

	public abstract String[] getFields();

	public Uri getUri() {
		return Uri.withAppendedPath(getBaseUri(), Long.toString(getId()));
	}

	public Uri getBaseUri() {
		return Uri.withAppendedPath(
				Uri.parse(ItemProvider.SCHEME
						+ ItemProvider.AUTHORITY), getTableName());
	}

	public void notifyProvider(final Context context) {
		try {
			context.getContentResolver().notifyChange(getUri(), null, false);
		} catch (UnsupportedOperationException e) {
			// Catch this for test suite. Mock provider cant notify
		}
	}

}


================================================
FILE: app/dbgenout/com/nononsenseapps/notepad/database/DatabaseHandler.java
================================================
package com.nononsenseapps.notepad.database;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * Database handler, SQLite wrapper and ORM layer.
 */
public class DatabaseHandler extends SQLiteOpenHelper {

	// All Static variables
	// Database Version
	private static final int DATABASE_VERSION = 1;

	// Database Name
	private static final String DATABASE_NAME = "SampleDB";
	private final Context context;

	private static DatabaseHandler instance = null;

	public synchronized static DatabaseHandler getInstance(Context context) {
		if (instance == null)
			instance = new DatabaseHandler(context.getApplicationContext());
		return instance;
	}

	public DatabaseHandler(Context context) {
		super(context.getApplicationContext(), DATABASE_NAME, null,
				DATABASE_VERSION);
		this.context = context.getApplicationContext();
	}

	@Override
	public void onOpen(SQLiteDatabase db) {
		super.onOpen(db);
		if (!db.isReadOnly()) {
			// Enable foreign key constraints
			// This line requires android16
			// db.setForeignKeyConstraintsEnabled(true);
			// This line works everywhere though
			db.execSQL("PRAGMA foreign_keys=ON;");

			// Create temporary triggers and views
			DatabaseTriggers.createTemp(db);
			DatabaseViews.createTemp(db);
		}
	}

	@Override
	public synchronized void onCreate(SQLiteDatabase db) {

		db.execSQL("DROP TABLE IF EXISTS " + taskviewItem.TABLE_NAME);
		db.execSQL(taskviewItem.CREATE_TABLE);


		// Create Triggers and Views
		DatabaseTriggers.create(db);
		DatabaseViews.create(db);
	}

	// Upgrading database
	@Override
	public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion,
									   int newVersion) {
		// Try to drop and recreate. You should do something clever here
		onCreate(db);
	}

	// Convenience methods
	public synchronized boolean putItem(final DBItem item) {
		boolean success = false;
		int result = 0;
		final SQLiteDatabase db = this.getWritableDatabase();
		final ContentValues values = item.getContent();

		if (item.getId() > -1) {
			result += db.update(item.getTableName(), values,
					DBItem.COL_ID + " IS ?",
					new String[] { String.valueOf(item.getId()) });
		}

		// Update failed or wasn't possible, insert instead
		if (result < 1) {
			final long id = db.insert(item.getTableName(), null, values);

			if (id > 0) {
				item.setId(id);
				success = true;
			}
		} else {
			success = true;
		}

		if (success) {
			item.notifyProvider(context);
		}
		return success;
	}

	public synchronized int deleteItem(DBItem item) {
		final SQLiteDatabase db = this.getWritableDatabase();
		final int result = db.delete(item.getTableName(), DBItem.COL_ID
				+ " IS ?", new String[] { Long.toString(item.getId()) });

		if (result > 0) {
			item.notifyProvider(context);
		}

		return result;
	}


	public synchronized Cursor gettaskviewItemCursor(final long id) {
		final SQLiteDatabase db = this.getReadableDatabase();
		final Cursor cursor = db.query(taskviewItem.TABLE_NAME,
				taskviewItem.FIELDS, taskviewItem.COL_ID + " IS ?",
				new String[] { String.valueOf(id) }, null, null, null, null);
		return cursor;
	}

	public synchronized taskviewItem gettaskviewItem(final long id) {
		final Cursor cursor = gettaskviewItemCursor(id);
		final taskviewItem result;
		if (cursor.moveToFirst()) {
			result = new taskviewItem(cursor);
		} else {
			result = null;
		}

		cursor.close();
		return result;
	}

	public synchronized Cursor getAlltaskviewItemsCursor(final String selection,
														 final String[] args,
														 final String sortOrder) {
		final SQLiteDatabase db = this.getReadableDatabase();

		final Cursor cursor = db.query(taskviewItem.TABLE_NAME,
				taskviewItem.FIELDS, selection, args, null, null, sortOrder, null);

		return cursor;
	}

	public synchronized List<taskviewItem> getAlltaskviewItems(final String selection,
															   final String[] args,
															   final String sortOrder) {
		final List<taskviewItem> result = new ArrayList<taskviewItem>();

		final Cursor cursor = getAlltaskviewItemsCursor(selection, args, sortOrder);

		while (cursor.moveToNext()) {
			taskviewItem q = new taskviewItem(cursor);
			result.add(q);
		}

		cursor.close();
		return result;
	}

}


================================================
FILE: app/dbgenout/com/nononsenseapps/notepad/database/DatabaseTriggers.java
================================================
package com.nononsenseapps.notepad.database;

import android.database.sqlite.SQLiteDatabase;

public class DatabaseTriggers {

	/**
	 * Create permanent triggers. They are dropped first,
	 * if they already exist.
	 */
	public static void create(final SQLiteDatabase db) {

	}

	/**
	 * Create temporary triggers. Nothing is done if they
	 * already exist.
	 */
	public static void createTemp(final SQLiteDatabase db) {

	}


}

================================================
FILE: app/dbgenout/com/nononsenseapps/notepad/database/DatabaseViews.java
================================================
package com.nononsenseapps.notepad.database;

import android.database.sqlite.SQLiteDatabase;

public class DatabaseViews {

	/**
	 * Create permanent views. They are dropped first,
	 * if they already exist.
	 */
	public static void create(final SQLiteDatabase db) {

	}

	/**
	 * Create temporary views. Nothing is done if they
	 * already exist.
	 */
	public static void createTemp(final SQLiteDatabase db) {
		db.execSQL(taskview_template);
	}

	private static final String taskview_template =
			"CREATE TEMP VIEW IF NOT EXISTS taskview_template AS SELECT _id FROM taskfts WHERE ftstable MATCH 'textfilter';";
}

================================================
FILE: app/dbgenout/com/nononsenseapps/notepad/database/ItemProvider.java
================================================
package com.nononsenseapps.notepad.database;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;

public class ItemProvider extends ContentProvider {
	public static final String AUTHORITY = "com.nononsenseapps.notepad.database.AUTHORITY";
	public static final String SCHEME = "content://";

	private static final UriMatcher sURIMatcher = new UriMatcher(
			UriMatcher.NO_MATCH);

	static {
		taskviewItem.addMatcherUris(sURIMatcher);
	}

	@Override
	public boolean onCreate() {
		return true;
	}


	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// Setup some common parsing and stuff
		final String table;
		final ContentValues values = new ContentValues();
		final ArrayList<String> args = new ArrayList<String>();
		if (selectionArgs != null) {
			for (String arg : selectionArgs) {
				args.add(arg);
			}
		}
		final StringBuilder sb = new StringBuilder();
		if (selection != null && !selection.isEmpty()) {
			sb.append("(").append(selection).append(")");
		}

		// Configure table and args depending on uri
		switch (sURIMatcher.match(uri)) {

			case taskviewItem.BASEITEMCODE:
				table = taskviewItem.TABLE_NAME;
				if (selection != null && !selection.isEmpty()) {
					sb.append(" AND ");
				}
				sb.append(taskviewItem.COL_ID + " IS ?");
				args.add(uri.getLastPathSegment());
				// Alternative is this
				// values.put(taskviewItem.COL_DELETED, 1);
				break;

			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}

		// Write to DB
		final SQLiteDatabase db = DatabaseHandler.getInstance(getContext())
				.getWritableDatabase();
		final String[] argArray = new String[args.size()];
		final int result = db.delete(table, sb.toString(),
				args.toArray(argArray));
		// Or alternatively
		//final int result = db.update(table, values, sb.toString(),
		//        args.toArray(argArray));

		if (result > 0) {
			// Support upload sync
			getContext().getContentResolver().notifyChange(uri, null, true);
		}
		return result;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// TODO: Implement this to handle requests to insert a new row.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
					  String[] selectionArgs) {
		// TODO: Implement this to handle requests to update one or more rows.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public String getType(Uri uri) {
		switch (sURIMatcher.match(uri)) {

			case taskviewItem.BASEITEMCODE:
				return taskviewItem.TYPE_ITEM;
			case taskviewItem.BASEURICODE:
				return taskviewItem.TYPE_DIR;
			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
						String[] args, String sortOrder) {
		Cursor result = null;
		final long id;
		final DatabaseHandler handler = DatabaseHandler.getInstance(getContext());

		switch (sURIMatcher.match(uri)) {

			case taskviewItem.BASEITEMCODE:
				id = Long.parseLong(uri.getLastPathSegment());
				result = handler.gettaskviewItemCursor(id);
				result.setNotificationUri(getContext().getContentResolver(), uri);
				break;
			case taskviewItem.BASEURICODE:
				result = handler.getAlltaskviewItemsCursor(selection, args, sortOrder);
				result.setNotificationUri(getContext().getContentResolver(), uri);
				break;

			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}

		return result;
	}
}


================================================
FILE: app/dbgenout/com/nononsenseapps/notepad/database/taskviewItem.java
================================================
package com.nononsenseapps.notepad.database;

import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;

/**
 * Represents taskview in the database.
 */
public class taskviewItem extends DBItem {
	public static final String TABLE_NAME = "taskview";

	public static Uri URI() {
		return Uri.withAppendedPath(
				Uri.parse(ItemProvider.SCHEME
						+ ItemProvider.AUTHORITY), TABLE_NAME);
	}

	// Column names
	public static final String COL_ID = "_id";
	public static final String COL_TITLE = "title";
	public static final String COL_FILTERTEXT = "filterText";
	public static final String COL_FILTERDUEEARLIEST = "filterDueEarliest";
	public static final String COL_FILTERDUELATEST = "filterDueLatest";
	public static final String COL_FILTERCOMPLETED = "filterCompleted";

	// For database projection so order is consistent
	public static final String[] FIELDS = { COL_ID, COL_TITLE, COL_FILTERTEXT, COL_FILTERDUEEARLIEST, COL_FILTERDUELATEST, COL_FILTERCOMPLETED };

	public Long _id = -1;
	public String title = "";
	public String filterText = "";
	public Long filterDueEarliest;
	public Long filterDueLatest;
	public Long filterCompleted;

	public static final int BASEURICODE = 0x2d72bbd;
	public static final int BASEITEMCODE = 0x824cd6d;

	public static void addMatcherUris(UriMatcher sURIMatcher) {
		sURIMatcher.addURI(ItemProvider.AUTHORITY, TABLE_NAME, BASEURICODE);
		sURIMatcher.addURI(ItemProvider.AUTHORITY, TABLE_NAME + "/#", BASEITEMCODE);
	}

	public static final String TYPE_DIR = "vnd.android.cursor.dir/vnd.com.nononsenseapps.notepad.database." + TABLE_NAME;
	public static final String TYPE_ITEM = "vnd.android.cursor.item/vnd.com.nononsenseapps.notepad.database." + TABLE_NAME;

	public taskviewItem() {
		super();
	}

	public taskviewItem(final Cursor cursor) {
		super();
		// Projection expected to match FIELDS array
		this._id = cursor.getLong(0);
		this.title = cursor.getString(1);
		this.filterText = cursor.getString(2);
		this.filterDueEarliest = cursor.isNull(3) ? null : cursor.getLong(3);
		this.filterDueLatest = cursor.isNull(4) ? null : cursor.getLong(4);
		this.filterCompleted = cursor.isNull(5) ? null : cursor.getLong(5);
	}

	public ContentValues getContent() {
		ContentValues values = new ContentValues();

		values.put(COL_TITLE, title);
		values.put(COL_FILTERTEXT, filterText);
		if (filterDueEarliest != null) {
			values.put(COL_FILTERDUEEARLIEST, filterDueEarliest);
		} else {
			values.putNull(COL_FILTERDUEEARLIEST);
		}
		if (filterDueLatest != null) {
			values.put(COL_FILTERDUELATEST, filterDueLatest);
		} else {
			values.putNull(COL_FILTERDUELATEST);
		}
		if (filterCompleted != null) {
			values.put(COL_FILTERCOMPLETED, filterCompleted);
		} else {
			values.putNull(COL_FILTERCOMPLETED);
		}

		return values;
	}

	public String getTableName() {
		return TABLE_NAME;
	}

	public String[] getFields() {
		return FIELDS;
	}

	public long getId() {
		return _id;
	}

	public void setId(final long id) {
		_id = id;
	}

	public static final String CREATE_TABLE =
			"CREATE TABLE taskview"
					+ "  (_id INTEGER PRIMARY KEY,"
					+ "  title TEXT NOT NULL DEFAULT '',"
					+ "  filterText TEXT NOT NULL DEFAULT '',"
					+ "  filterDueEarliest INTEGER,"
					+ "  filterDueLatest INTEGER,"
					+ "  filterCompleted INTEGER"
					+ ""
					+ "  )";
}


================================================
FILE: app/dbsetup.py
================================================
"""Generate a sample project with triggers"""

from AndroidCodeGenerator.generator import Generator
from AndroidCodeGenerator.sql_validator import SQLTester
from AndroidCodeGenerator.db_table import (Table, Column, ForeignKey, Unique,
                                           Trigger, Check)
from AndroidCodeGenerator.database_triggers import DatabaseTriggers

tables = []
triggers = []

tasklists = Table('tasklist')
tasklists.add_cols(Column('title').text.not_null.default("''"),
                   Column('updated').integer,
                   Column('listtype').text,
                   Column('sorting').text,
                   Column('deleted').integer.not_null.default(0),
                   # New fields
                   Column('ctime').timestamp.default_current_timestamp,
                   Column('mtime').timestamp.default_current_timestamp,
                   # GTask fields
                   Column('account').text,
                   Column('remoteid').text)
tasklists.add_constraints(Unique('account', 'remoteid').on_conflict_replace)

tables.append(tasklists)

tasks = Table('task')
tasks.add_cols(Column('title').text.not_null.default("''"),
               Column('note').text.not_null.default("''"),
               Column('completed').integer,
               Column('updated').integer,
               Column('due').integer,
               Column('locked').integer.not_null.default(0),
               Column('deleted').integer.not_null.default(0),
               # Positions
               Column('posleft').integer.not_null.default(1),
               Column('posright').integer.not_null.default(2),
               Column('tasklist').integer.not_null,
               # New fields
               Column('ctime').timestamp.default_current_timestamp,
               Column('mtime').timestamp.default_current_timestamp,
               # GTask fields
               Column('account').text,
               Column('remoteid').text)


tasks.add_constraints(ForeignKey('tasklist').references(tasklists.name)\
                      .on_delete_cascade,
                      Unique('account', 'remoteid').on_conflict_replace,
                      Check('posleft', '> 0'),
                      Check('posright', '> 1'))

tables.append(tasks)

log = Table('history')
log.add_cols(Column('taskid').integer,
             Column('title').text.not_null.default("''"),
             Column('note').text.not_null.default("''"),
             Column('deleted').integer.not_null.default(0),
             Column('updated').timestamp.not_null.default_current_timestamp)
log.add_constraints(ForeignKey('taskid').references(tasks.name)\
                    .on_delete_set_null)

tables.append(log)

for name in ['tr_up_history', 'tr_ins_history',
             'tr_del_history']:
    t = Trigger(name).temp.if_not_exists
    deleted = 'new.deleted'
    if 'up' in name:
        t.after.update_on(tasks.name)
    elif 'ins' in name:
        t.after.insert_on(tasks.name)
    else:
        t.before.delete_on(tasks.name)
        deleted = 1
    t.do_sql("INSERT INTO {} \
(taskid, title, note, deleted) \
VALUES (new._id, new.title, new.note, {})".format(log.name, deleted))
    triggers.append(t)

# Notifications
nots = Table('notification')
nots.add_cols(Column('time').integer,
             Column('permanent').integer.not_null.default(0),
             Column('taskid').integer,
             Column('repeats').integer.not_null.default(0),
             Column('locationname').text,
             Column('latitude').real,
             Column('longitude').real,
             Column('radius').real)
nots.add_constraints(ForeignKey('taskid').references(tasks.name)\
                    .on_delete_cascade)
tables.append(nots)

# Let's try to create the SQL
st = SQLTester()
st.add_tables(*tables)
st.add_triggers(*triggers)

st.test_create()

#g = Generator(path='./sample/src/com/example/appname/database/')
#g.add_tables(persons, log)
#g.add_triggers(trigger)
#g.write()


================================================
FILE: app/proguard.pro
================================================
# Add project specific ProGuard rules here.
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# I only care about minimizing
-dontobfuscate

# Everything in the app is essential
-keep class com.nononsenseapps.** { *; }
-keep public class com.nononsenseapps.** { *; }
-keep class com.mobeta.android.** { *; }

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# gradle (and stackoverflow) agree that suppressing the warning is appropriate
-dontwarn org.joda.convert.**


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/BaseTestClass.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

import androidx.preference.PreferenceManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;

import com.nononsenseapps.helpers.NnnLogger;
import com.nononsenseapps.notepad.activities.main.ActivityMain_;
import com.nononsenseapps.notepad.database.DatabaseHandler;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;

public class BaseTestClass {

	/**
	 * A JUnit {@link Rule @Rule} to launch your activity under test. This replaces
	 * ActivityInstrumentationTestCase2. Rules are executed for each test method and will run
	 * before any of your setup code in the @Before method. To get a reference to the activity
	 * you can use: {@link IntentsTestRule#getActivity()}
	 * <br/>
	 * NOTE: the newer alternative, {@link ActivityScenarioRule}, <b>DOES NOT WORK</b>
	 */
	@SuppressWarnings("deprecation")
	@Rule
	public IntentsTestRule<ActivityMain_> mActRule;


	/**
	 * Since API 33 we need permission for notifications
	 */
	@Rule
	public GrantPermissionRule mNotifRule;

	/**
	 * @return a string with the content of the given resourceId
	 */
	public String getStringResource(int resourceId) {
		return mActRule.getActivity().getString(resourceId);
	}

	@After
	public void clearAppData() {
		if (mActRule != null) mActRule.finishActivity();
		Context context = ApplicationProvider.getApplicationContext();

		// clear the app's data as the test is starting & finishing
		PreferenceManager.getDefaultSharedPreferences(context).edit().clear().commit();
		DatabaseHandler.resetDatabase(context);
	}

	/**
	 * Tries in many ways to give notification permission for OS versions that need it
	 */
	private void giveNotifyPermission() {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
			// this permission works only on API >= 33, it crashes on older versions!
			mNotifRule = GrantPermissionRule.grant(Manifest.permission.POST_NOTIFICATIONS);

			String command = "pm grant " +
					ApplicationProvider.getApplicationContext().getPackageName() + " " +
					Manifest.permission.POST_NOTIFICATIONS;

			// this one is more likely to work
			InstrumentationRegistry
					.getInstrumentation()
					.getUiAutomation()
					.executeShellCommand(command);
		} else {
			mNotifRule = null;
		}
	}

	/**
	 * Many times, on the github VM, the tests fail with RootViewWithoutFocusException,
	 * I think it's due to the emulator being slow. Let's launch the activity and wait
	 * for it to load before starting the real test
	 */
	@Before
	public void launchAndWait() {
		// ensure that this is called BEFORE trying to start the activity
		clearAppData();

		// first, acquire all the required permissions ...
		giveNotifyPermission();

		// ... then, create and run the entry point to the app
		mActRule = new IntentsTestRule<>(ActivityMain_.class);
		Intent launchApp = new Intent(ApplicationProvider.getApplicationContext(), ActivityMain_.class);
		mActRule.launchActivity(launchApp);

		try {
			// it responds => we can return now
			onView(isRoot()).check(matches(isDisplayed()));
			return;
		} catch (Exception e) {
			NnnLogger.error(this.getClass(), "Activity isn't responsive:");
			NnnLogger.exception(e);
		}

		// maybe we just have to wait
		EspressoHelper.waitUi();

		// if it's still not enough, let's crash here
		try {
			onView(isRoot()).check(matches(isDisplayed()));
		} catch (Exception e) {
			NnnLogger.error(this.getClass(), "Can't launch activity:");
			NnnLogger.exception(e);
			throw e;
		}
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/EspressoHelper.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.RootMatchers.isDialog;
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.instanceOf;

import android.app.UiAutomation;
import android.os.SystemClock;

import androidx.annotation.IdRes;
import androidx.test.espresso.AmbiguousViewMatcherException;
import androidx.test.espresso.Espresso;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.contrib.DrawerActions;
import androidx.test.platform.app.InstrumentationRegistry;

import com.getkeepsafe.taptargetview.TapTargetView;
import com.nononsenseapps.helpers.NnnLogger;
import com.nononsenseapps.notepad.activities.main.ActivityMain;
import com.nononsenseapps.notepad.R;

import org.junit.Assert;

public class EspressoHelper {

	/**
	 * Wait for 350ms to work around timing issues on slow emulators. It's called to solve issues
	 * with flaky tests on github runners. Sometime tests need it, sometimes they don't,
	 * but you can't know. It should go after every call to {@link ViewInteraction#perform}
	 */
	public static void waitUi() {
		InstrumentationRegistry.getInstrumentation().waitForIdleSync();
		SystemClock.sleep(400);
	}

	/**
	 * open the drawer on the left
	 */
	public static void openDrawer() {
		try {
			onView(withId(R.id.drawerLayout)).check(matches(isDisplayed()));
		} catch (Exception e) {
			Assert.fail("Can't find the drawerLayout, maybe a dialog is still open?");
			return;
		}
		onView(withId(R.id.drawerLayout)).perform(DrawerActions.open());
	}

	public static void createNoteWithName(String noteName) {
		onView(withId(R.id.menu_add))
				.check(matches(isDisplayed()))
				.check(matches(isClickable()));
		onView(withId(R.id.menu_add)).perform(click());
		EspressoHelper.hideShowCaseViewIfShown();
		onView(withId(R.id.taskText)).perform(typeText(noteName));
	}

	/**
	 * Presses "+" and writes text for each note given in noteNames
	 */
	public static void createNotes(String[] noteNames) {
		for (String noteName : noteNames) {
			createNoteWithName(noteName);
		}
	}

	/**
	 * Add a new task list. The drawer should be open when this is called
	 *
	 * @param taskListName name of the task list
	 */
	public static void createTaskList(String taskListName) {
		EspressoHelper.openDrawer();

		// dismiss the other showcase view
		EspressoHelper.hideShowCaseViewIfShown();

		onView(withId(R.id.drawer_menu_createlist)).check(matches(isDisplayed()));
		// sometimes the automator will hold the button too long. This will show the tooltip,
		// and the test will fail because the button did not get the click to show the dialog.
		// It's a matter of luck: retry the test and it will work
		onView(withId(R.id.drawer_menu_createlist)).perform(click());
		waitUi(); // the popup may need time to load

		// the text field in the dialog visibile must be visible
		onViewWithIdInDialog(R.id.titleField).check(matches(isDisplayed()));

		// fill the popup
		onViewWithIdInDialog(R.id.titleField).perform(typeText(taskListName));
		onViewWithIdInDialog(R.id.dialog_yes).check(matches(isDisplayed()));
		onViewWithIdInDialog(R.id.dialog_yes).perform(closeSoftKeyboard());
		onViewWithIdInDialog(R.id.dialog_yes).perform(click());
	}

	/**
	 * shorthand for onView(withId(viewId)).inRoot(isDialog())
	 *
	 * @param viewId it's R.id.something
	 */
	public static ViewInteraction onViewWithIdInDialog(@IdRes int viewId) {
		return onView(withId(viewId)).inRoot(isDialog());
	}

	/**
	 * @return TRUE if the app is in tablet mode, FALSE in phone mode
	 */
	public static boolean isInTabletMode() {
		boolean isInPortraitMode = InstrumentationRegistry
				.getInstrumentation()
				.getTargetContext()
				.getResources()
				.getBoolean(R.bool.fillEditor);
		return !isInPortraitMode;
	}

	public static void navigateUp() {
		onView(isRoot()).perform(closeSoftKeyboard());
		if (isInTabletMode()) {
			// we are in tablet mode: press "+" to make a note appear in the list
			onView(withId(R.id.menu_add)).perform(click());
		} else {
			// we are in phone mode: close the keyboard & press the back button
			Espresso.pressBack();
		}
	}

	/**
	 * Exits the "settings" activity, going back to {@link ActivityMain}
	 */
	public static void exitPrefsActivity() {

		String label = InstrumentationRegistry
				.getInstrumentation()
				.getTargetContext()
				.getString(R.string.menu_preferences);
		try {
			// TODO improve & return if necessary
			onView(withText(label)).check(matches(isDisplayed()));
		} catch (Exception e) {
			NnnLogger.warning(EspressoHelper.class, "Can't determine if PrefsActivity is shown:");
			NnnLogger.exception(e);
		}

		// for now, assume this function is called only when a fragment of PrefsActivity is shown

		if (isInTabletMode()) {
			// tablets show menu & category => press back only once
			Espresso.pressBack();
		} else {
			// in phones, go back to the menu, then back to ActivityMain
			Espresso.pressBack();
			Espresso.pressBack();
		}
	}

	/**
	 * @return TRUE if the {@link TapTargetView} is currently shown, FALSE otherwise
	 */
	private static Boolean isShowCaseOverlayVisible() {
		try {
			onView(instanceOf(TapTargetView.class)).check(matches(isDisplayed()));
			return true;
		} catch (AmbiguousViewMatcherException ignored) {
			// we have at least one, so it counts as visible
			return true;
		} catch (Throwable ignored) {
			// it has to be "Throwable", not "Exception"
			return false;
		}
	}

	/**
	 * If the {@link TapTargetView} is shown, touch the screen to hide it, so that tests
	 * can then interact with the app.
	 */
	public static void hideShowCaseViewIfShown() {
		waitUi();
		if (!EspressoHelper.isShowCaseOverlayVisible()) return;

		// click anywhere to dismiss it
		try {
			onView(instanceOf(TapTargetView.class)).perform(click());
		} catch (Exception ignored) {
			Assert.fail("Could not dismiss the TapTargetView");
			return;
		}
		waitUi();
	}

	/**
	 * Rotate the screen twice, waiting ~4 seconds for the animations to finish.
	 * It automatically understands if the phone or tablet is "naturally" held in
	 * landscape or portrait mode, so test should be done with the emulator's default
	 * settings: phones in portrait mode and tablets in landscape mode
	 */
	public static void rotateScreenAndWait() {
		var uiAuto = InstrumentationRegistry
				.getInstrumentation()
				.getUiAutomation();
		// rotate it
		uiAuto.setRotation(UiAutomation.ROTATION_FREEZE_270);
		// wait 1s for the rotation to finish
		waitUi();
		waitUi();

		// rotating the screen sometimes makes the taptargetview appear in the wrong place.
		// I have no idea why. In any case, we have to close it now, or else the next
		// screen rotation will make the app crash. Yes it's incomprehensible, but now it works
		EspressoHelper.hideShowCaseViewIfShown();

		// rotate it more
		uiAuto.setRotation(UiAutomation.ROTATION_FREEZE_0);
		waitUi();
		waitUi();

		// unfreeze it and let it go back to its default state
		uiAuto.setRotation(UiAutomation.ROTATION_UNFREEZE);
		waitUi();
		waitUi();
		waitUi();
	}

}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddBigNumberOfNotesScrollDownAndDeleteOne.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static android.view.View.FIND_VIEWS_WITH_TEXT;
import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.hamcrest.Matchers.instanceOf;

import android.view.View;
import android.widget.ListView;

import androidx.test.espresso.ViewAssertion;
import androidx.test.espresso.matcher.CursorMatchers;
import androidx.test.filters.LargeTest;

import com.mobeta.android.dslv.DragSortListView;
import com.nononsenseapps.notepad.R;

import org.junit.Test;

import java.util.ArrayList;

@LargeTest
public class TestAddBigNumberOfNotesScrollDownAndDeleteOne extends BaseTestClass {

	private final String[] noteNameList = {
			"prepare food", "take dogs out", "water plants", "sleep",
			"go for a jog", "do some work", "play with the dog",
			"work out", "do weird stuff", "read a book", "drink water",
			"write a book", "proofread the book", "publish the book",
			"ponder life", "build a house", "repair the house", "call contractor",
			"write another book", "scrap the book project", "start a blog",
			"  ", "     "
	};

	/**
	 * credit to <a href="https://gist.github.com/chemouna/00b10369eb1d5b00401b">Chemouna @ GitHub</a>
	 */
	private static ViewAssertion doesNotHaveViewWithText(final String text) {
		return (view, e) -> {
			if (!(view instanceof ListView rv)) {
				throw e;
			}
			ArrayList<View> outviews = new ArrayList<>();
			for (int index = 0; index < rv.getAdapter().getCount(); index++) {
				rv
						//.findViewHolderForAdapterPosition(index)
						//.itemView
						.findViewsWithText(outviews, text, FIND_VIEWS_WITH_TEXT);
				if (!outviews.isEmpty()) break;
			}
			assertTrue(outviews.isEmpty());
		};
	}

	@Test
	public void testAddBigNumberOfNotesScrollDownAndDeleteOne() {
		EspressoHelper.hideShowCaseViewIfShown();

		EspressoHelper.createNotes(noteNameList);

		// exit the note editing mode
		EspressoHelper.navigateUp();

		// click on the bottom-most note
		onData(CursorMatchers
					.withRowString("title", equalToIgnoringCase(noteNameList[0])))
				.inAdapterView(allOf(
						hasMinimumChildCount(1),
						instanceOf(DragSortListView.class)))
				.perform(scrollTo())
				.perform(click());

		// delete the note
		onView(withId(R.id.menu_delete)).perform(click());
		EspressoHelper.waitUi();
		onView(withId(android.R.id.button1)).perform(click());

		// check that the 1° note added was deleted
		onView(allOf(withId(android.R.id.list), isDisplayed()))
				.check(doesNotHaveViewWithText(noteNameList[0]));

		// if the showcaseview is visible when closing the app, there will be a crash
		EspressoHelper.hideShowCaseViewIfShown();
	}


}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteShouldShowNameInNotesScreen.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.equalToIgnoringCase;

import androidx.test.filters.LargeTest;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestAddNewNoteShouldShowNameInNotesScreen extends BaseTestClass {

	private String noteName1;

	@Before
	public void initStrings() {
		noteName1 = "prepare food";
	}

	@Test
	public void testAddNewNoteShouldShowNameInNotesScreen() {
		EspressoHelper.hideShowCaseViewIfShown();

		EspressoHelper.createNoteWithName(noteName1);
		EspressoHelper.navigateUp();

		onView(withText(equalToIgnoringCase(noteName1))).check(matches(isDisplayed()));
	}

}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithDueDateCheckDateIsVisible.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.allOf;

import androidx.test.filters.LargeTest;

import com.nononsenseapps.notepad.R;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestAddNewNoteWithDueDateCheckDateIsVisible extends BaseTestClass {

	private String noteName1;

	@Before
	public void initStrings() {
		noteName1 = "prepare food";

	}

	@Test
	public void testAddNewNoteWithDueDateCheckDateIsVisible() {
		EspressoHelper.hideShowCaseViewIfShown();

		EspressoHelper.createNoteWithName(noteName1);
		onView(withId(R.id.dueDateBox)).perform(click());
		onView(withId(android.R.id.button1)).perform(click());

		EspressoHelper.navigateUp();

		// target only the dateview in the note shown in the "tasks" list
		var dateView = onView(allOf(withId(R.id.date), isCompletelyDisplayed()));
		dateView.check(matches(isDisplayed()));
	}
}

================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithReminderDateAndTime.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import androidx.test.filters.LargeTest;

import com.nononsenseapps.notepad.R;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestAddNewNoteWithReminderDateAndTime extends BaseTestClass {

	private String noteName1;

	@Before
	public void initStrings() {
		noteName1 = "prepare food";
	}

	@Test
	public void testAddNewNoteWithReminderDateAndTime() {
		EspressoHelper.hideShowCaseViewIfShown();

		EspressoHelper.createNoteWithName(noteName1);

		//add reminder
		onView(withId(R.id.notificationAdd)).perform(click());
		EspressoHelper.waitUi();

		//add date
		onView(withId(R.id.notificationDate)).perform(click());
		EspressoHelper.waitUi();
		onView(withId(android.R.id.button1)).perform(click());

		//add time
		onView(withId(com.nononsenseapps.notepad.R.id.notificationTime)).perform(click());
		onView(withId(android.R.id.button1)).perform(click());

		EspressoHelper.navigateUp();

		//check that the date field is visible
		onView(withText(equalToIgnoringCase(noteName1))).perform(click());
		onView(withId(R.id.notificationDate)).check(matches(isDisplayed()));

		// maybe we should also check someting like
		// onView(withId(R.id.notificationDate)).check(matches(withText("november 10")));
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNoteToTaskList.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;

import androidx.test.filters.LargeTest;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestAddNoteToTaskList extends BaseTestClass {

	private String taskListName, noteName1;

	@Before
	public void initStrings() {
		taskListName = "a random task list";
		noteName1 = "prepare food";
	}

	@Test
	public void testAddNoteToTaskList() {

		EspressoHelper.hideShowCaseViewIfShown();
		EspressoHelper.createTaskList(taskListName);

		// make sure the correct task list is selected
		EspressoHelper.openDrawer();
		onView(allOf(withText(taskListName), withId(android.R.id.text1)))
				.check(matches(isDisplayed()));
		onView(allOf(withText(taskListName), withId(android.R.id.text1)))
				.perform(click());
		EspressoHelper.waitUi();

		// add the note
		EspressoHelper.createNoteWithName(noteName1);
		EspressoHelper.navigateUp();

		// make sure that the number of notes for the task list is actually 1
		EspressoHelper.openDrawer();
		onView(allOf(withText(taskListName), hasSibling(withText("1"))))
				.check(matches(withText(taskListName)));

	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNotesAndRotateScreen.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import androidx.test.filters.LargeTest;

import com.nononsenseapps.ui.TitleNoteTextView;

import org.junit.Before;
import org.junit.Test;


@LargeTest
public class TestAddNotesAndRotateScreen extends BaseTestClass {

	String noteName1, noteName2, noteName3, noteName4;

	@Before
	public void initStrings() {
		noteName1 = "prepare food";
		noteName2 = "take dogs out";
		noteName3 = "water plants";
		noteName4 = "sleep";
	}

	@Test
	public void testAddNotesAndRotateScreen() {
		EspressoHelper.hideShowCaseViewIfShown();

		String[] noteNames = { noteName1, noteName2, noteName3, noteName4 };

		EspressoHelper.createNotes(noteNames);
		EspressoHelper.navigateUp();

		EspressoHelper.rotateScreenAndWait();

		// in case it's still there
		EspressoHelper.hideShowCaseViewIfShown();

		// check that textviews still show up.
		onView(allOf(
					instanceOf(TitleNoteTextView.class),
					withText(equalToIgnoringCase(noteNames[0]))))
				.check(matches(isDisplayed()));
		onView(allOf(
					instanceOf(TitleNoteTextView.class),
					withText(equalToIgnoringCase(noteNames[1]))))
				.check(matches(isDisplayed()));
		onView(allOf(
					instanceOf(TitleNoteTextView.class),
					withText(equalToIgnoringCase(noteNames[2]))))
				.check(matches(isDisplayed()));
		onView(allOf(
					instanceOf(TitleNoteTextView.class),
					withText(equalToIgnoringCase(noteNames[3]))))
				.check(matches(isDisplayed()));
	}
}




================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListAndRotateScreen.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.hamcrest.Matchers.allOf;

import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.filters.LargeTest;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestAddTaskListAndRotateScreen extends BaseTestClass {

	private String taskListName;

	@Before
	public void initStrings() {
		taskListName = "a random task list";
	}

	@Test
	public void testAddTaskListAndRotateScreen() {
		EspressoHelper.hideShowCaseViewIfShown();
		EspressoHelper.createTaskList(taskListName);

		EspressoHelper.openDrawer();

		EspressoHelper.rotateScreenAndWait();

		// make sure the task list is still visible.
		// if the rotations didn't finish, it will crash here
		RecyclerViewActions
				.scrollTo(hasDescendant(withText(equalToIgnoringCase(taskListName))));
		onView(allOf(
					withText(equalToIgnoringCase(taskListName)),
					withId(android.R.id.text1)))
				.check(matches(isDisplayed()));
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListCheckItIsAddedToDrawer.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;

import androidx.test.filters.LargeTest;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestAddTaskListCheckItIsAddedToDrawer extends BaseTestClass {

	private String taskListName;

	@Before
	public void initStrings() {
		taskListName = "a random task list";
	}

	@Test
	public void testAddTaskListCheckItIsAddedToDrawer() {
		EspressoHelper.hideShowCaseViewIfShown();

		EspressoHelper.createTaskList(taskListName);
		EspressoHelper.openDrawer();

		//check that the new note is found and has the correct text
		onView(allOf(withText(taskListName), withId(android.R.id.text1)))
				.check(matches(isDisplayed()));
	}


}



================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListsScrollNavigationDrawer.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;

import static org.hamcrest.Matchers.equalToIgnoringCase;

import androidx.test.espresso.matcher.CursorMatchers;
import androidx.test.filters.LargeTest;

import com.nononsenseapps.notepad.R;

import org.junit.Test;

@LargeTest
public class TestAddTaskListsScrollNavigationDrawer extends BaseTestClass {

	final String[] taskListNames = { "Lorem", "ipsum ", "dolor ", "sit ", "amet", "consectetur ",
			"adipiscing ", "elit", "sed ", "do ", "eiusmod ", "tempor ", "incididunt ",
			"ut ", "labore " };

	@Test
	public void testAddTaskListsScrollNavigationDrawer() {
		String SETTINGS_TEXT = getStringResource(R.string.menu_preferences);
		String SETTINGS_APPEARANCE_TEXT = getStringResource(R.string.settings_cat_appearance);

		EspressoHelper.hideShowCaseViewIfShown();

		for (String name : taskListNames) {
			EspressoHelper.createTaskList(name);
			EspressoHelper.openDrawer();
		}
		EspressoHelper.openDrawer();

		// onData() can scroll to the item, but can't click it
		onData(CursorMatchers
				.withRowString("title", equalToIgnoringCase("ut ")))
				.inAdapterView(withId(R.id.leftDrawer))
				.perform(scrollTo())
				.check(matches(isDisplayed()));

		// onView() can click on the item, but can't scroll to it
		onView(allOf(
					withText(equalToIgnoringCase("ut ")),
					withId(android.R.id.text1)))
				.perform(click());

		EspressoHelper.openDrawer();

		// open the preferences page and check that it is visible
		openContextualActionModeOverflowMenu();
		onView(withText(SETTINGS_TEXT)).perform(click());
		onView(withText(SETTINGS_APPEARANCE_TEXT))
				.check(matches(isDisplayed()));
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCompletedTasksAreCleared.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.matcher.ViewMatchers.hasChildCount;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;

import androidx.test.espresso.DataInteraction;
import androidx.test.espresso.Espresso;
import androidx.test.espresso.PerformException;
import androidx.test.filters.LargeTest;

import com.nononsenseapps.notepad.R;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestCompletedTasksAreCleared extends BaseTestClass {

	String noteName1, noteName2, noteName3, noteName4;

	@Before
	public void initStrings() {
		noteName1 = "prepare food";
		noteName2 = "take dogs out";
		noteName3 = "water plants";
		noteName4 = "sleep";
	}

	@Test
	public void testCompletedTasksAreCleared() {
		EspressoHelper.hideShowCaseViewIfShown();

		String[] noteNames = { noteName1, noteName2, noteName3, noteName4 };
		EspressoHelper.createNotes(noteNames);

		EspressoHelper.navigateUp();
		clickCheckBoxAt(1);
		clickCheckBoxAt(3);

		//clear notes
		openContextualActionModeOverflowMenu();
		String CLEAR_COMPLETED = getStringResource(R.string.menu_clearcompleted);
		onView(withText(CLEAR_COMPLETED)).perform(click());
		onView(withId(android.R.id.button1)).perform(click());

		//check that the notes do not exist any more
		onView(withText(noteNames[0]))
				.check(doesNotExist());
		onView(withText(noteNames[2]))
				.check(doesNotExist());
	}

	/**
	 * this function expects the list to have 5 children (=notes): the 4 added in this test
	 * + the default 'welcome' note
	 */
	private void clickCheckBoxAt(int position) {
		// the keyboard may cover the notes, which makes the next lines crash!
		try {
			Espresso.closeSoftKeyboard();
		} catch (PerformException ignored) {
			// keyboard was already closed
		}
		DataInteraction di = onData(anything())
				.inAdapterView(allOf(withId(android.R.id.list), hasChildCount(5)))
				.atPosition(position)
				.onChildView(withId(R.id.checkbox));
		di.perform(click());
	}

}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateNoteAndDeleteIt.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.equalToIgnoringCase;

import androidx.test.filters.LargeTest;

import com.nononsenseapps.notepad.R;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestCreateNoteAndDeleteIt extends BaseTestClass {

	private String noteName1;

	@Before
	public void initStrings() {
		noteName1 = "prepare food";
	}

	@Test
	public void testCreateNoteAndDeleteIt() {
		EspressoHelper.hideShowCaseViewIfShown();

		EspressoHelper.createNoteWithName(noteName1);
		EspressoHelper.navigateUp();

		onView(withText(equalToIgnoringCase(noteName1))).perform(click());
		onView(withId(R.id.menu_delete)).perform(click());
		onView(withId(android.R.id.button1)).perform(click());

		// assert that we're back in the list
		onView(withId(R.id.menu_search)).check(matches(isDisplayed()));

		//check that the view is not visible
		onView(withText(equalToIgnoringCase(noteName1))).check(doesNotExist());
	}

}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateTaskListAndDeleteIt.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.matcher.RootMatchers.isDialog;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;

import androidx.test.filters.LargeTest;

import com.nononsenseapps.notepad.R;

import org.junit.Before;
import org.junit.Test;

@LargeTest
public class TestCreateTaskListAndDeleteIt extends BaseTestClass {

	private String taskListName;

	@Before
	public void initStrings() {
		taskListName = "a random task list";
	}

	@Test
	public void testCreateTaskListAndDeleteIt() {

		EspressoHelper.hideShowCaseViewIfShown();
		EspressoHelper.createTaskList(taskListName);

		EspressoHelper.openDrawer();

		onView(allOf(withText(taskListName), withId(android.R.id.text1))).perform(longClick());
		EspressoHelper.waitUi();

		onView(isRoot()).inRoot(isDialog()).perform(closeSoftKeyboard());
		onView(withId(R.id.deleteButton)).perform(click());

		onView(withText(this.getStringResource(android.R.string.ok))).perform(click());
		onView(withText(taskListName)).check(doesNotExist());
	}

}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestPasswords.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.equalToIgnoringCase;

import com.nononsenseapps.notepad.R;
import com.nononsenseapps.ui.TitleNoteTextView;

import org.junit.Test;

public class TestPasswords extends BaseTestClass {

	// when the note is locked, only this is shown
	final String noteTitle = "this is my note";

	// note tile + content, shown in the edittext
	final String fullNoteText1 = noteTitle + "\n.\ncontent line";

	// typed into the popup
	final String password = "itsnotasecrettoanybody";

	@Test
	public void testAddNoteLockWithPassword() {
		EspressoHelper.hideShowCaseViewIfShown();

		EspressoHelper.createNoteWithName(fullNoteText1);
		EspressoHelper.navigateUp();

		onView(withText(equalToIgnoringCase(fullNoteText1))).check(matches(isDisplayed()));
		onView(withText(equalToIgnoringCase(fullNoteText1))).perform(click());
		openContextualActionModeOverflowMenu();

		String MENU_TEXT = getStringResource(R.string.lock_note);
		onView(withText(MENU_TEXT)).perform(click());

		EspressoHelper
				.onViewWithIdInDialog(R.id.passwordField)
				.perform(typeText(password));
		EspressoHelper
				.onViewWithIdInDialog(R.id.passwordVerificationField)
				.perform(typeText(password));
		EspressoHelper
				.onViewWithIdInDialog(R.id.dialog_yes)
				.perform(click());
		EspressoHelper.waitUi();

		// then it opens the popup again, to ask the password
		EspressoHelper
				.onViewWithIdInDialog(R.id.passwordField)
				.check(matches(isDisplayed()));
		EspressoHelper
				.onViewWithIdInDialog(R.id.passwordField)
				.perform(typeText(password));
		EspressoHelper
				.onViewWithIdInDialog(R.id.dialog_yes)
				.check(matches(isDisplayed()));
		EspressoHelper
				.onViewWithIdInDialog(R.id.dialog_yes)
				.perform(click());

		// the note on the (custom) edittext should appear correctly
		onView(withId(R.id.taskText))
				.check(matches(withText(equalToIgnoringCase(fullNoteText1))));
		EspressoHelper.navigateUp();

		// in the list view, only the title is shown
		onView(allOf(
					withText(equalToIgnoringCase(noteTitle)),
					instanceOf(TitleNoteTextView.class)))
				.check(matches(isDisplayed()));
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestSaveLoadJsonBackup.java
================================================
package com.nononsenseapps.notepad.espresso_tests;

import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.matcher.ViewMatchers.hasChildCount;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

import androidx.documentfile.provider.DocumentFile;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.nononsenseapps.notepad.R;

import org.junit.Test;

@LargeTest
public class TestSaveLoadJsonBackup extends BaseTestClass {

	final String noteText1 = "random note";
	final String noteText2 = "other random note";

	/**
	 * @return an intent that replicates the response of the system's filepicker,
	 * using a custom fileprovider
	 */
	private static Instrumentation.ActivityResult createResponseIntent() {
		Context context = InstrumentationRegistry
				.getInstrumentation()
				.getTargetContext();

		// TODO here you have to create a uri for a documentfile that represents a
		//  folder where we can write files. good luck! There's no explanation of this anywhere

		Uri uri1 = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3Agoogletest%2Ftest_outputfiles");
		var docDir = DocumentFile.fromTreeUri(context, uri1);
		var uri2 = docDir.getUri();

		Intent i = new Intent()
				.setData(uri2)
				.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
				.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
		return new Instrumentation.ActivityResult(Activity.RESULT_OK, i);
	}

	@Test
	public void testSaveLoadBackup() {
		EspressoHelper.hideShowCaseViewIfShown();

		// add 2 notes
		EspressoHelper.createNoteWithName(noteText1);
		EspressoHelper.createNoteWithName(noteText2);

		EspressoHelper.navigateUp();

		// make a backup
		openContextualActionModeOverflowMenu();
		String SETTINGS_TEXT = getStringResource(R.string.menu_preferences);
		onView(withText(SETTINGS_TEXT))
				.perform(scrollTo()) // in case the keyboard is covering the menu
				.perform(click());

		String SETTINGS_BACKUP_TEXT = getStringResource(R.string.backup);
		onView(withText(SETTINGS_BACKUP_TEXT)).perform(click());

		// register answer to mock the filepicker
		intending(hasAction(Intent.ACTION_OPEN_DOCUMENT_TREE))
				.respondWith(createResponseIntent());

		// choose a backup directory
		String CHOOSE_DIR = getStringResource(R.string.choose_backup_folder);
		onView(withText(CHOOSE_DIR)).perform();

		// then click export
		String EXPORT_BACKUP_TEXT = getStringResource(R.string.backup_export);
		onView(withText(EXPORT_BACKUP_TEXT)).perform(click());
		EspressoHelper.onViewWithIdInDialog(android.R.id.button1).check(matches(isDisplayed()));
		EspressoHelper.onViewWithIdInDialog(android.R.id.button1).perform(click());
		EspressoHelper.waitUi();

		// return to the notes list
		EspressoHelper.exitPrefsActivity();

		// from here on, it does not work. check createResponseIntent()
		if (true) return;

		// check & delete both notes
		clickCheckBoxAt(0);
		clickCheckBoxAt(1);

		// clear completed notes
		openContextualActionModeOverflowMenu();
		String CLEAR_COMPLETED = getStringResource(R.string.menu_clearcompleted);
		onView(withText(CLEAR_COMPLETED)).perform(click());
		EspressoHelper.onViewWithIdInDialog(android.R.id.button1).check(matches(isDisplayed()));
		EspressoHelper.onViewWithIdInDialog(android.R.id.button1).perform(click());

		// restore the backup
		openContextualActionModeOverflowMenu();
		onView(withText(SETTINGS_TEXT)).perform(click());
		onView(withText(SETTINGS_BACKUP_TEXT)).perform(click());

		String IMPORT_BACKUP_TEXT = getStringResource(R.string.backup_import);
		onView(withText(IMPORT_BACKUP_TEXT)).perform(click());
		EspressoHelper.onViewWithIdInDialog(android.R.id.button1).perform(click());
		EspressoHelper.waitUi();

		// return to the notes list
		EspressoHelper.exitPrefsActivity();

		// ensure both notes were restored
		onView(withText(noteText1)).check(matches(isDisplayed()));
		onView(withText(noteText2)).check(matches(isDisplayed()));
	}

	// TODO explore accessibility tests. See
	//  https://developer.android.com/training/testing/espresso/accessibility-checking

	// this one expects the list to have 2 children
	private void clickCheckBoxAt(int position) {
		var i = onData(anything())
				.inAdapterView(allOf(withId(android.R.id.list), hasChildCount(2)))
				.atPosition(position)
				.onChildView(withId(R.id.checkbox));
		i.perform(click());
	}

}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBFreshTest.java
================================================
package com.nononsenseapps.notepad.test;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.nononsenseapps.notepad.database.DatabaseHandler;
import com.nononsenseapps.notepad.database.LegacyDBHelper;
import com.nononsenseapps.notepad.database.Task;
import com.nononsenseapps.notepad.database.TaskList;

import junit.framework.TestCase;

public class DBFreshTest extends TestCase {

	static final String PREFIX = "fresh_test_";

	private Context context;

	@Override
	public void setUp() throws Exception {
		super.setUp();
		context = InstrumentationRegistry.getInstrumentation().getTargetContext();
	}

	@Override
	public void tearDown() throws Exception {
		super.tearDown();
	}

	@SmallTest
	public void testFreshInstall() {
		context.deleteDatabase(PREFIX + LegacyDBHelper.LEGACY_DATABASE_NAME);
		context.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME);
		final SQLiteDatabase db = new DatabaseHandler(context, PREFIX).getReadableDatabase();
		// Just open the database, there should be one list and one task present
		Cursor tlc = db.query(TaskList.TABLE_NAME, TaskList.Columns.FIELDS,
				null, null, null, null, null);

		assertEquals("Should be ONE list present on fresh installs",
				1, tlc.getCount());
		tlc.close();

		Cursor tc = db.query(Task.TABLE_NAME, Task.Columns.FIELDS,
				null, null, null, null, null);
		assertEquals("Should be 1 task present on fresh installs, the 'welcome' task",
				1, tc.getCount());
		tc.close();

		db.close();
		assertTrue("Could not delete database",
				context.deleteDatabase(PREFIX + LegacyDBHelper.LEGACY_DATABASE_NAME));
		assertTrue("Could not delete database",
				context.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME));
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderMovementTest.java
================================================
package com.nononsenseapps.notepad.test;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteConstraintException;
import android.net.Uri;
import android.util.Log;

import androidx.test.platform.app.InstrumentationRegistry;

import com.nononsenseapps.notepad.database.DAO;
import com.nononsenseapps.notepad.database.Task;
import com.nononsenseapps.notepad.database.TaskList;

import junit.framework.TestCase;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Locale;
import java.util.Random;

public class DBProviderMovementTest extends TestCase {

	private ContentResolver resolver;


	@Override
	public void setUp() throws Exception {
		super.setUp();
		resolver = InstrumentationRegistry
				.getInstrumentation()
				.getTargetContext()
				.getContentResolver();
	}

	@Override
	public void tearDown() throws Exception {
		super.tearDown();
	}

	/*
	 * Util methods
	 */

	private void assertUriReturnsResult(final Uri uri, final String[] fields) {
		assertUriReturnsResult(uri, fields, null, null);
	}

	private void assertUriReturnsResult(final Uri uri, final String[] fields,
										final String where, final String[] whereArgs) {
		final Cursor c = resolver.query(uri, fields, where, whereArgs, null);
		final boolean notEmpty = c.moveToFirst();
		c.close();
		assertTrue("Uri did not return a result: " + uri.getEncodedPath(),
				notEmpty);
	}

	private Cursor assertCursorGood(final Cursor c) {
		assertNotNull(c);
		assertFalse(c.isClosed());

		return c;
	}

	private void assertTasksCountIs(final long listId, final int count) {
		assertEquals(count, getTasks(listId).size());
	}

	private void assertTaskLeftRightAreSequential(final long listId) {
		// Get ordered
		ArrayList<Task> tasks = getTasks(listId);
		long prev = 0;
		for (Task t : tasks) {
			assertTrue("Left must be less than right! " + t.left + " !< "
					+ t.right, t.left < t.right);
			assertTrue("Previous item must have smaller left",
					prev < t.left);
			if (t.right == t.left + 1) {
				prev = t.right;
			} else {
				prev = t.left;
			}
		}

		// Test maximum value
		Cursor c = resolver.query(Task.URI, Task.Columns.FIELDS,
				Task.Columns.DBLIST + " IS ?",
				new String[] { Long.toString(listId) }, Task.Columns.RIGHT + " DESC");
		assertCursorGood(c);
		HashSet<Long> positions = new HashSet<>();
		if (c.getCount() > 0) {
			// Right most will be twice the number of tasks
			assertTrue(c.moveToFirst());
			final Task last = new Task(c);

			assertEquals(String.format(Locale.US, "%d != 2 * %d", last.right, c.getCount()),
					(long) last.right, 2L * c.getCount());

			// Make sure there are no duplicates and such
			for (long i = 1; i <= c.getCount() * 2L; i++) {
				positions.add(i);
			}

			positions.remove(last.left);
			positions.remove(last.right);

			while (c.moveToNext()) {
				Task task = new Task(c);
				positions.remove(task.left);
				positions.remove(task.right);
			}
		}
		c.close();

		assertEquals("Must be duplicate positions in the list", 0,
				positions.size());
	}

	private TaskList insertList() {
		final TaskList tl = new TaskList();
		tl.title = "A test list";

		tl.setId(resolver.insert(tl.getBaseUri(), tl.getContent()));

		assertTrue(0 < tl._id);

		return tl;
	}

	private void deleteList(final TaskList tl) {
		assertTrue(0 < resolver.delete(tl.getUri(), null, null));
	}

	private ArrayList<Task> insertTasks(final long listId, final int number) {
		ArrayList<Task> results = new ArrayList<>(number);
		for (int i = 0; i < number; i++) {
			int count = 0;
			Task t = new Task();
			t.title = "Task" + ++count;
			t.dblist = listId;
			Uri uri = resolver.insert(Task.URI, t.getContent());
			if (uri != null) {
				t.setId(uri);
				results.add(t);
			}
			assertTaskLeftRightAreSequential(listId);
		}
		assertTaskLeftRightAreSequential(listId);
		assertTasksCountIs(listId, number);

		return results;
	}

	private ArrayList<Task> getTasks(final long listId) {
		final ArrayList<Task> results = new ArrayList<>();
		final Cursor c = resolver.query(Task.URI,
				Task.Columns.FIELDS, Task.Columns.DBLIST + " IS ?",
				new String[] { Long.toString(listId) }, Task.Columns.LEFT);
		assertCursorGood(c);
		while (c.moveToNext()) {
			results.add(new Task(c));
		}
		c.close();

		return results;
	}

	private Task getTask(final long id) {
		final Cursor c = resolver.query(Task.getUri(id), Task.Columns.FIELDS,
				null, null, null);
		assertCursorGood(c);
		Task t = null;
		if (c.moveToFirst()) {
			t = new Task(c);
		}
		c.close();

		return t;
	}

	private ArrayList<Task> getDeletedTask(final String title) {
		final ArrayList<Task> results = new ArrayList<>();
		final Cursor c = resolver.query(Task.URI_DELETED_QUERY,
				Task.Columns.FIELDS, Task.Columns.TITLE + "IS ?",
				new String[] { title }, null);
		assertCursorGood(c);
		while (c.moveToNext()) {
			results.add(new Task(c));
		}
		c.close();

		return results;
	}

	private void deleteTasks(final ArrayList<Task> tasks) {
		for (final Task t : tasks) {
			deleteTask(t);
		}
	}

	private void deleteTask(Task t) {
		assertTrue(0 < resolver.delete(t.getUri(), null, null));
		assertTaskLeftRightAreSequential(t.dblist);
	}

	private void moveTasksToList(final TaskList tl, final Task... ts) {
		long[] ids = new long[ts.length];
		for (int i = 0; i < ts.length; i++) {
			ids[i] = ts[i]._id;
		}

		final ContentValues val = new ContentValues();
		val.put(Task.Columns.DBLIST, tl._id);

		// where _ID in (1, 2, 3)
		final String whereId = Task.Columns._ID + " IN (" + DAO.arrayToCommaString(ids) + ")";

		var mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
		mContext.getContentResolver().update(Task.URI, val, whereId, null);

		// Verify that task was moved
		// Check new
		assertTaskLeftRightAreSequential(tl._id);
	}

	private ArrayList<Task> moveAndAssert(final TaskList tl, final int fromPos,
										  final int toPos) {
		Log.i("nononsenseapps test", "Testing move from: " + fromPos + " to "
				+ toPos);
		// Get ordered
		final ArrayList<Task> oldtasks = getTasks(tl._id);

		// Move 5 to 4
		final Task movingTask = oldtasks.get(fromPos);
		final Task targetTask = oldtasks.get(toPos);

		final int result = movingTask.moveTo(resolver, targetTask);

		// Verity that things changed or not
		if (movingTask._id != targetTask._id)
			assertTrue("Moving a task should update rows", 0 < result);
		else
			assertEquals("Moving a task to itself shouldn't change anything", 0, result);

		// Find new values
		final ArrayList<Task> newtasks = getTasks(tl._id);
		Task newone = null;
		Task newtarget = null;

		for (Task t : newtasks) {
			if (t._id == movingTask._id) {
				newone = t;
			}
			if (t._id == targetTask._id) {
				newtarget = t;
			}
		}
		Log.d("nononsenseapps test", "old, target, new, newtarget: "
				+ movingTask.left + "," + movingTask.right + " "
				+ targetTask.left + "," + targetTask.right + " " + newone.left
				+ "," + newone.right + " " + newtarget.left + ","
				+ newtarget.right);

		assertNotNull("Couldnt find the moved task", newone);

		if (targetTask.left < movingTask.left) {
			assertEquals("Left value does not equal target", targetTask.left,
					newone.left);
			assertEquals("Right value does not equal target left + 1",
					targetTask.left + 1, (long) newone.right);
		} else if (targetTask.right > movingTask.right) {
			assertEquals("Left value does not equal target right - 1",
					targetTask.right - 1, (long) newone.left);
			assertEquals("Right value does not equal target", targetTask.right,
					newone.right);
		}

		assertEquals("Width should be 1 after a move", 1, newone.right
				- newone.left);

		// assertEquals("Target should have moved 2 steps", 2,);

		assertEquals("Number of tasks should not change", oldtasks.size(), newtasks.size());
		assertTaskLeftRightAreSequential(tl._id);

		return getTasks(tl._id);
	}

	/*
	 * Test methods
	 */

	public void testDeleteList() {
		final TaskList tl = insertList();
		final int count = 10;
		final long listId = tl._id;

		assertTasksCountIs(listId, 0);

		insertTasks(listId, count);
		assertTasksCountIs(listId, count);

		deleteList(tl);
		// Should return nothing
		// Cursor c = resolver.query(TaskList.URI, TaskList.Columns.FIELDS,
		// TaskList.Columns._ID + " IS ?",
		// new String[] { Long.toString(listId) }, null);
		// assertCursorGood(c);

		// assertEquals("List should be gone", 0, c.getCount());
		// removing list should delete all tasks within
		assertTasksCountIs(listId, 0);
		// c.close();
	}

	public void testInsertAndRemoveTasks() {
		final TaskList tl = insertList();
		ArrayList<Task> tasks = insertTasks(tl._id, 10);
		assertTasksCountIs(tl._id, 10);
		deleteTasks(tasks);
		assertTasksCountIs(tl._id, 0);
		deleteList(tl);
	}

	public void testInsertTaskInWrongList() {
		// Should not be possible to insert
		// into a non-existing list because of foreign key constraints

		final long wrongId = 92525;
		assertTasksCountIs(wrongId, 0);

		Task t = new Task();
		t.title = "Task";
		t.dblist = wrongId;

		boolean thrown = false;
		Uri uri = null;
		try {
			uri = resolver.insert(Task.URI, t.getContent());
		} catch (SQLException e) {
			thrown = true;
		}

		assertNull(uri);
		assertTasksCountIs(wrongId, 0);
	}

	public void testInvalidPos() {
		// Positions must be greater than 0
		// or should throw constraint failed

		TaskList tl = insertList();
		ArrayList<Task> ts = insertTasks(tl._id, 1);

		Task t = ts.get(0);

		t.left = 0L;

		boolean failed = false;
		try {
			resolver.update(t.getUri(), t.getContent(), null, null);
		} catch (SQLiteConstraintException e) {
			failed = true;
		}

		//assertTrue("Setting left to 0 should throw exception!", failed);

		t.left = 5L;
		t.right = 0L;
		failed = false;
		try {
			resolver.update(t.getUri(), t.getContent(), null, null);
		} catch (SQLiteConstraintException e) {
			failed = true;
		}
		//assertTrue("Setting right to 0 should throw exception", failed);

		deleteList(tl);
	}

	public void testMoveTask() {
		final TaskList tl = insertList();
		int count = 10;
		insertTasks(tl._id, count);
		assertTaskLeftRightAreSequential(tl._id);

		// Move some tasks around
		moveAndAssert(tl, 0, count - 1);
		moveAndAssert(tl, count - 1, 0);

		moveAndAssert(tl, 1, count - 2);
		moveAndAssert(tl, count - 2, 1);

		moveAndAssert(tl, 4, 0);
		moveAndAssert(tl, 4, 9);

		for (int i = 0; i < count * 2; i++) {
			moveAndAssert(tl, 0, count - 1);
		}
		for (int i = 0; i < count * 2; i++) {
			moveAndAssert(tl, count - 2, 2);
		}

		Random rand = new Random();
		int min = 0, max = count - 1;
		for (int i = 0; i < count * 2; i++) {
			int fromPos = rand.nextInt(max - min + 1) + min;
			int toPos = fromPos;
			while (toPos == fromPos) {
				toPos = rand.nextInt(max - min + 1) + min;
			}
			// two unique positions generated, now move
			moveAndAssert(tl, fromPos, toPos);
		}

		// Clean up
		deleteList(tl);
	}

	public void testMoveTaskToList() {
		final TaskList tl = insertList();
		final TaskList tl2 = insertList();
		int count = 10;
		ArrayList<Task> tasks1 = insertTasks(tl._id, count);
		ArrayList<Task> tasks2 = insertTasks(tl2._id, count);
		assertTaskLeftRightAreSequential(tl._id);
		assertTaskLeftRightAreSequential(tl2._id);

		// Move some tasks around


		Random rand = new Random();
		int min = 0, max = count - 1;
		for (int i = 0; i < 100; i++) {
			if (rand.nextBoolean() && tasks1.size() > 1) {
				int taskIndex = rand.nextInt(tasks1.size());
				Task t1 = tasks1.remove(taskIndex);
				taskIndex = rand.nextInt(tasks1.size());
				Task t2 = tasks1.remove(taskIndex);
				moveTasksToList(tl2, t1, t2);
				tasks2.add(t1);
				tasks2.add(t2);
			} else if (tasks2.size() > 1) {
				int taskIndex = rand.nextInt(tasks2.size());
				Task t1 = tasks2.remove(taskIndex);
				taskIndex = rand.nextInt(tasks2.size());
				Task t2 = tasks2.remove(taskIndex);
				moveTasksToList(tl, t1, t2);
				tasks1.add(t1);
				tasks1.add(t2);
			}
		}

		// Clean up
		deleteList(tl);
		deleteList(tl2);
	}

//	public void testIndents() {
//		final TaskList tl = insertList();
//		int count = 7;
//		insertTasks(tl._id, count);
//		ArrayList<Task> orgTasks = getTasks(tl._id);
//
//		// Indenting the first item should fail (not change anything)
//		// as it's impossible to do
//
//		indentAndAssert(orgTasks.get(0), false);
//
//		// Test a successful one
//		/*
//		 * a0 b1 c0 d0 e0
//		 */
//		orgTasks = indentAndAssert(orgTasks.get(1), true);
//		assertEquals("Task level should be two", 2, orgTasks.get(1).level);
//
//		// Indenting it again should fail though
//		/*
//		 * a0 b1 c0 d0 e0 f0 g0
//		 */
//		orgTasks = indentAndAssert(orgTasks.get(1), false);
//		assertEquals("Task level should still be two", 2, orgTasks.get(1).level);
//
//		// Try last item
//		/*
//		 * a0 b1 c0 d0 e0 f0 g1
//		 */
//		orgTasks = indentAndAssert(orgTasks.get(count - 1), true);
//		assertEquals("Task level should be two", 2,
//				orgTasks.get(count - 1).level);
//
//		/*
//		 * a0 b1 c2 d1 e0 f1 g2
//		 */
//		orgTasks = indentAndAssert(orgTasks.get(2), true);
//		orgTasks = indentAndAssert(orgTasks.get(2), true);
//		orgTasks = indentAndAssert(orgTasks.get(2), false);
//		assertEquals("Task level should be three", 3, orgTasks.get(2).level);
//		orgTasks = indentAndAssert(orgTasks.get(3), true);
//		assertEquals("Task level incorrect", 2, orgTasks.get(3).level);
//		orgTasks = indentAndAssert(orgTasks.get(5), true);
//		orgTasks = indentAndAssert(orgTasks.get(5), false);
//		assertEquals("Task level incorrect", 2, orgTasks.get(5).level);
//		orgTasks = indentAndAssert(orgTasks.get(6), true);
//		orgTasks = indentAndAssert(orgTasks.get(6), false);
//		assertEquals("Task level incorrect", 3, orgTasks.get(6).level);
//
//		/*
//		 * a0 b1 c2 d3 e4 f5 g6
//		 */
//		orgTasks = indentAndAssert(orgTasks.get(0), false);
//		orgTasks = indentAndAssert(orgTasks.get(1), false);
//		orgTasks = indentAndAssert(orgTasks.get(2), false);
//		orgTasks = indentAndAssert(orgTasks.get(3), true);
//		orgTasks = indentAndAssert(orgTasks.get(3), true);
//		orgTasks = indentAndAssert(orgTasks.get(3), false);
//		orgTasks = indentAndAssert(orgTasks.get(4), true);
//		orgTasks = indentAndAssert(orgTasks.get(4), true);
//		orgTasks = indentAndAssert(orgTasks.get(4), true);
//		orgTasks = indentAndAssert(orgTasks.get(4), true);
//		orgTasks = indentAndAssert(orgTasks.get(4), false);
//		orgTasks = indentAndAssert(orgTasks.get(5), true);
//		orgTasks = indentAndAssert(orgTasks.get(5), true);
//		orgTasks = indentAndAssert(orgTasks.get(5), true);
//		orgTasks = indentAndAssert(orgTasks.get(5), true);
//		orgTasks = indentAndAssert(orgTasks.get(5), false);
//		orgTasks = indentAndAssert(orgTasks.get(6), true);
//		orgTasks = indentAndAssert(orgTasks.get(6), true);
//		orgTasks = indentAndAssert(orgTasks.get(6), true);
//		orgTasks = indentAndAssert(orgTasks.get(6), true);
//		orgTasks = indentAndAssert(orgTasks.get(6), false);
//
//		for (int i = 0; i < orgTasks.size(); i++) {
//			assertEquals("Task level incorrect", 1 + i, orgTasks.get(i).level);
//		}
//
//		// Let's start unindenting stuff!
//
//		// Unindent root should fail
//		orgTasks = unIndentAndAssert(orgTasks.get(0), false);
//		// Last one should succeed many times
//		for (int i = orgTasks.size(); i > 1; i--) {
//			assertEquals("Level incorrect (i = " + i + ")", i,
//					orgTasks.get(orgTasks.size() - 1).level);
//			orgTasks = unIndentAndAssert(orgTasks.get(orgTasks.size() - 1),
//					true);
//		}
//		// Should now be a root
//		assertEquals("Level incorrect", 1,
//				orgTasks.get(orgTasks.size() - 1).level);
//
//		// Let's do the rest, top to bottom
//		// All preceeding items are affected by this amount
//		int cum = 0;
//		for (int j = 1; j < orgTasks.size() - 1; j++) {
//			for (int i = j + 1; i - cum > 1; i--) {
//				assertEquals("Level incorrect (i = " + i + ")", i - cum,
//						orgTasks.get(j).level);
//				orgTasks = unIndentAndAssert(orgTasks.get(j), true);
//			}
//			cum += 1;
//			// Should now be a root
//			assertEquals("Level incorrect", 1, orgTasks.get(j).level);
//		}
//
//		deleteList(tl);
//	}
//
//	public void testIndentsHarder() {
//		// Was something I actually did and noticed a crash
//		final TaskList tl = insertList();
//		int count = 3;
//		insertTasks(tl._id, count);
//		ArrayList<Task> orgTasks = getTasks(tl._id);
//
//		// The issue is the order things are moved in the statement
//		// Moved in order of their IDs!
//
//		// Now at 2,1,0
//		// Want 0,2,1 (where 2 is indented)
//		orgTasks = moveAndAssert(tl, 2, 0);
//		orgTasks = indentAndAssert(orgTasks.get(1), true);
//
//		// This crashed by trying to set right to null
//		// Since the parent was re-assigned before the child
//		orgTasks = unIndentAndAssert(orgTasks.get(1), true);
//
//		deleteList(tl);
//	}
//
//	public void testMoveIndentedTrees() {
//		// important to test moving tasks in a tree structure
//		final TaskList tl = insertList();
//		int count = 9;
//		insertTasks(tl._id, count);
//		ArrayList<Task> orgTasks = getTasks(tl._id);
//
//		// Roots at 0, 2 and 6
//		// 0
//		indentAndAssert(orgTasks.get(1), true);
//		// 2
//		indentAndAssert(orgTasks.get(3), true);
//		indentAndAssert(orgTasks.get(4), true);
//		indentAndAssert(orgTasks.get(4), true);
//		indentAndAssert(orgTasks.get(5), true);
//		indentAndAssert(orgTasks.get(5), true);
//		indentAndAssert(orgTasks.get(5), true);
//		// 6
//		indentAndAssert(orgTasks.get(7), true);
//		indentAndAssert(orgTasks.get(8), true);
//		indentAndAssert(orgTasks.get(8), true);
//
//		// move 2 tree to bottom
//		// now part of six tree
//		moveAndAssert(tl, 2, 8);
//
//		// Move 6 tree to top
//		moveAndAssert(tl, 3, 0);
//
//		// Move 2 tree to top
//		moveAndAssert(tl, 6, 0);
//
//		deleteList(tl);
//	}


	public void testTaskContent() {
		Task t = new Task();
		t.title = "Hej";
		t.dblist = 1L;
		ContentValues values = t.getContent();

		assertFalse(values.containsKey(Task.Columns.LEFT));
		assertFalse(values.containsKey(Task.Columns.RIGHT));
	}

	public void testDeleteTrigger() {
		// Deleting an item should place a copy of it in the delete-table
		final TaskList tl = insertList();
		insertTasks(tl._id, 1);
		ArrayList<Task> orgTasks = getTasks(tl._id);

		final Task orgTask = orgTasks.get(0);

		final String t = "HABANA MAMAMANA";
		orgTask.title = t;

		resolver.update(orgTask.getUri(), orgTask.getContent(), null, null);
		resolver.delete(orgTask.getUri(), null, null);

		orgTasks = getTasks(tl._id);

		assertEquals("List should be empty now", 0, orgTasks.size());

		// Get the item from backup table instead
		Cursor c = resolver.query(Task.URI_DELETED_QUERY,
				Task.Columns.DELETEFIELDS, Task.Columns.TITLE + " IS ?",
				new String[] { t }, null);

		assertTrue("Task should be found in delete table after delete!",
				c.moveToFirst());

		deleteList(tl);

		c.close();
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderTest.java
================================================
package com.nononsenseapps.notepad.test;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.SystemClock;

import androidx.preference.PreferenceManager;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.nononsenseapps.helpers.NnnLogger;
import com.nononsenseapps.notepad.database.DatabaseHandler;
import com.nononsenseapps.notepad.database.Task;
import com.nononsenseapps.notepad.database.TaskList;

import junit.framework.TestCase;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

public class DBProviderTest extends TestCase {

	private Context mContext;
	private ContentResolver mResolver;

	@Override
	public void setUp() throws Exception {
		super.setUp();
		mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
		mResolver = mContext.getContentResolver();

		// clear app data
		PreferenceManager.getDefaultSharedPreferences(mContext).edit().clear().commit();
		DatabaseHandler.resetDatabase(mContext);
	}

	private void assertUriReturnsResult(final Uri uri, final String[] fields) {
		assertUriReturnsResult(uri, fields, null, null, -1);
	}

	private void assertUriReturnsResult(final Uri uri, final String[] fields, final String where,
										final String[] whereArgs, final int count) {
		final Cursor c = mResolver
				.query(uri, fields, where, whereArgs, null);
		assertNotNull(c);

		String READABLE_CURSOR_DUMP = DatabaseUtils.dumpCursorToString(c);
		NnnLogger.debug(DBProviderTest.class, READABLE_CURSOR_DUMP);

		final int cursorCount = c.getCount();
		c.close();
		if (count < 0) {
			assertTrue("Uri did not return a result: " + uri.getEncodedPath(),
					cursorCount > 0);
		} else {
			// I don't know, sometimes it happens...
			assertEquals("Uri did not return expected number of results!",
					count, cursorCount);
		}
	}

	private TaskList getNewList() {
		TaskList result = new TaskList();
		result.title = "111aaTestingList";
		result.save(mContext);
		return result;
	}

	private ArrayList<Task> insertSomeTasks(final TaskList list, final int count) {
		ArrayList<Task> tasks = new ArrayList<>();
		for (int i = 0; i < count; i++) {
			Task t = new Task();
			t.title = "testTask" + i;
			t.note = "testNote" + i;
			t.due = Calendar.getInstance().getTimeInMillis();
			t.dblist = list._id;
			t.save(mContext);
			tasks.add(t);
		}
		return tasks;
	}

	@MediumTest
	public void testTaskListURIs() {
		final TaskList list = getNewList();
		assertUriReturnsResult(TaskList.URI, TaskList.Columns.FIELDS);
		assertUriReturnsResult(TaskList.URI_WITH_COUNT, TaskList.Columns.FIELDS);
		list.delete(mContext);
	}

	@MediumTest
	public void testTaskURIs() {
		final TaskList list = getNewList();
		final int taskCount = 5;
		final List<Task> tasks = insertSomeTasks(list, taskCount);

		assertUriReturnsResult(Task.URI, Task.Columns.FIELDS);

		// maybe the next line call to assertUriReturnsResult() fails due to timing issues ?
		SystemClock.sleep(500);

		// Sectioned Date query
		assertUriReturnsResult(
				Task.URI_SECTIONED_BY_DATE,
				Task.Columns.FIELDS,
				// see issue #525: on some android devices, "dblist" is a column of type BLOB,
				// but we expect it to always be INTEGER. On older devices this cast is redundant,
				// but on newer OS versions it fixes the bug when selecting notes by due date
				"CAST(" + Task.Columns.DBLIST + " AS INTEGER) IS ?",
				new String[] { Long.toString(list._id) },
				taskCount + 1);

		// History query
		Task t = tasks.get(0);
		final int histCount = 22;
		for (int i = 0; i < 22; i++) {
			// edit the note & save it
			t.title += " hist" + i;
			t.save(mContext);
		}
		// Should return insert (1) + update count (histCount)
		assertUriReturnsResult(Task.URI_TASK_HISTORY,
				Task.Columns.HISTORY_COLUMNS, Task.Columns.HIST_TASK_ID
						+ " IS ?", new String[] { Long.toString(t._id) },
				histCount + 1);

		// TODO remember legacy uris
		// TODO need a projection mapper
		// assertUriReturnsResult(LegacyDBHelper.NotePad.Notes.CONTENT_URI,
		// new String[] { LegacyDBHelper.NotePad.Notes.COLUMN_NAME_TITLE });
		// assertUriReturnsResult(
		// LegacyDBHelper.NotePad.Notes.CONTENT_VISIBLE_URI,
		// new String[] { LegacyDBHelper.NotePad.Notes.COLUMN_NAME_TITLE });

		list.delete(mContext);

		// Should return insert NOTHING since it should have been deleted
		assertUriReturnsResult(Task.URI_TASK_HISTORY,
				Task.Columns.HISTORY_COLUMNS, Task.Columns.HIST_TASK_ID
						+ " IS ?", new String[] { Long.toString(t._id) }, 0);
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBUpgradeTest.java
================================================
package com.nononsenseapps.notepad.test;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.provider.BaseColumns;

import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.nononsenseapps.notepad.database.DatabaseHandler;
import com.nononsenseapps.notepad.database.LegacyDBHelper;
import com.nononsenseapps.notepad.database.LegacyDBHelper.NotePad;
import com.nononsenseapps.notepad.database.Notification;
import com.nononsenseapps.notepad.database.Task;
import com.nononsenseapps.notepad.database.TaskList;

import junit.framework.TestCase;

public class DBUpgradeTest extends TestCase {
	static final String PREFIX = "dbupgrade_test_";

	final String aTime = "2013-03-23T02:43:35.000Z";
	final String anId = "MDIwMzMwNjA0MjM5MzQ4MzIzMjU6MDow";
	final String anAccount = "fake@account.com";

	final int numOfLegacyLists = 2;
	final int numOfLegacyNotes = 4;

	private Context context;

	@Override
	public void setUp() throws Exception {
		super.setUp();
		context = InstrumentationRegistry.getInstrumentation().getTargetContext();
	}

	@Override
	public void tearDown() throws Exception {
		super.tearDown();
	}

	private void createTables(final SQLiteDatabase legacyDB) {
		// Lists
		legacyDB.execSQL("CREATE TABLE " + NotePad.Lists.TABLE_NAME + " ("
				+ BaseColumns._ID + " INTEGER PRIMARY KEY,"
				+ NotePad.Lists.COLUMN_NAME_TITLE
				+ " TEXT DEFAULT '' NOT NULL,"
				+ NotePad.Lists.COLUMN_NAME_MODIFIED
				+ " INTEGER DEFAULT 0 NOT NULL,"
				+ NotePad.Lists.COLUMN_NAME_MODIFICATION_DATE
				+ " INTEGER DEFAULT 0 NOT NULL,"
				+ NotePad.Lists.COLUMN_NAME_DELETED
				+ " INTEGER DEFAULT 0 NOT NULL" + ");");

		legacyDB.execSQL("CREATE TABLE " + NotePad.GTaskLists.TABLE_NAME + " ("
				+ BaseColumns._ID + " INTEGER PRIMARY KEY,"
				+ NotePad.GTaskLists.COLUMN_NAME_DB_ID
				+ " INTEGER UNIQUE NOT NULL REFERENCES "
				+ NotePad.Lists.TABLE_NAME + ","
				+ NotePad.GTaskLists.COLUMN_NAME_GTASKS_ID
				+ " INTEGER NOT NULL,"
				+ NotePad.GTaskLists.COLUMN_NAME_GOOGLE_ACCOUNT
				+ " INTEGER NOT NULL," + NotePad.GTaskLists.COLUMN_NAME_UPDATED
				+ " TEXT," + NotePad.GTaskLists.COLUMN_NAME_ETAG + " TEXT"
				+ ");");

		// Notes
		legacyDB.execSQL("CREATE TABLE " + NotePad.Notes.TABLE_NAME + " ("
				+ BaseColumns._ID + " INTEGER PRIMARY KEY,"
				+ NotePad.Notes.COLUMN_NAME_TITLE
				+ " TEXT DEFAULT '' NOT NULL," + NotePad.Notes.COLUMN_NAME_NOTE
				+ " TEXT DEFAULT '' NOT NULL,"
				+ NotePad.Notes.COLUMN_NAME_CREATE_DATE
				+ " INTEGER DEFAULT 0 NOT NULL,"
				+ NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE
				+ " INTEGER DEFAULT 0 NOT NULL,"
				+ NotePad.Notes.COLUMN_NAME_DUE_DATE + " TEXT,"
				+ NotePad.Notes.COLUMN_NAME_LIST
				+ " INTEGER NOT NULL REFERENCES " + NotePad.Lists.TABLE_NAME
				+ "," + NotePad.Notes.COLUMN_NAME_GTASKS_STATUS
				+ " TEXT NOT NULL," + NotePad.Notes.COLUMN_NAME_POSITION
				+ " TEXT," + NotePad.Notes.COLUMN_NAME_HIDDEN
				+ " INTEGER DEFAULT 0 NOT NULL,"
				+ NotePad.Notes.COLUMN_NAME_MODIFIED
				+ " INTEGER DEFAULT 0 NOT NULL,"

				+ NotePad.Notes.COLUMN_NAME_INDENTLEVEL
				+ " INTEGER DEFAULT 0 NOT NULL,"
				+ NotePad.Notes.COLUMN_NAME_POSSUBSORT
				+ " TEXT DEFAULT '' NOT NULL,"
				+ NotePad.Notes.COLUMN_NAME_LOCALHIDDEN + " INTEGER DEFAULT 0,"

				+ NotePad.Notes.COLUMN_NAME_PARENT + " TEXT,"

				+ NotePad.Notes.COLUMN_NAME_DELETED
				+ " INTEGER DEFAULT 0 NOT NULL" + ");");

		legacyDB.execSQL("CREATE TABLE " + NotePad.GTasks.TABLE_NAME + " ("
				+ BaseColumns._ID + " INTEGER PRIMARY KEY,"
				+ NotePad.GTasks.COLUMN_NAME_DB_ID
				+ " INTEGER UNIQUE NOT NULL REFERENCES " + NotePad.Notes.TABLE_NAME
				+ "," + NotePad.GTasks.COLUMN_NAME_GTASKS_ID
				+ " INTEGER NOT NULL,"
				+ NotePad.GTasks.COLUMN_NAME_GOOGLE_ACCOUNT
				+ " INTEGER NOT NULL," + NotePad.GTasks.COLUMN_NAME_UPDATED
				+ " TEXT," + NotePad.GTasks.COLUMN_NAME_ETAG + " TEXT" + ");");

		// Notifications
		legacyDB.execSQL("CREATE TABLE " + NotePad.Notifications.TABLE_NAME
				+ " (" + NotePad.Notifications._ID + " INTEGER PRIMARY KEY,"
				+ NotePad.Notifications.COLUMN_NAME_TIME
				+ " INTEGER NOT NULL DEFAULT 0,"
				+ NotePad.Notifications.COLUMN_NAME_PERMANENT
				+ " INTEGER NOT NULL DEFAULT 0,"
				+ NotePad.Notifications.COLUMN_NAME_NOTEID + " INTEGER,"
				+ "FOREIGN KEY(" + NotePad.Notifications.COLUMN_NAME_NOTEID
				+ ") REFERENCES " + NotePad.Notes.TABLE_NAME + "("
				+ NotePad.Notes._ID + ") ON DELETE CASCADE" + ");");
	}

	private void initializeDB(final SQLiteDatabase legacyDB) {

		legacyDB.beginTransaction();
		// Need to create the tables so we have something to test with.
		createTables(legacyDB);

		// Insert some lists, and some notes
		final ContentValues values = new ContentValues();
		for (int i = 0; i < numOfLegacyLists; i++) {
			values.clear();
			// One plain
			values.put(LegacyDBHelper.NotePad.Lists.COLUMN_NAME_TITLE, "List"
					+ i);
			values.put(LegacyDBHelper.NotePad.Lists.COLUMN_NAME_MODIFIED, 1);
			values.put(LegacyDBHelper.NotePad.Lists.COLUMN_NAME_DELETED, 0);

			final long listId = legacyDB.insert(
					LegacyDBHelper.NotePad.Lists.TABLE_NAME, null, values);

			assertTrue("Failed to insert legacy test list: " + listId,
					listId > 0);

			long gtasklistid = -1;
			// One with google id
			if (i % 2 == 0) {
				values.clear();
				values.put(LegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_DB_ID,
						listId);
				values.put(
						LegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_GOOGLE_ACCOUNT,
						anAccount);
				values.put(
						LegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_GTASKS_ID,
						anId);
				values.put(
						LegacyDBHelper.NotePad.GTaskLists.COLUMN_NAME_UPDATED,
						aTime);

				gtasklistid = legacyDB.insert(
						LegacyDBHelper.NotePad.GTaskLists.TABLE_NAME, null,
						values);

				assertTrue(
						"Failed to insert google dummy list: " + gtasklistid,
						gtasklistid > 0);
			}

			// Insert notes
			for (int j = 0; j < numOfLegacyNotes; j++) {
				values.clear();
				values.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_TITLE,
						"default" + j);
				values.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_NOTE,
						"defaulttext");
				values.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_MODIFIED, 1);
				values.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_DELETED, 0);
				values.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_LIST,
						listId);

				// Gets the current system time in milliseconds
				Long now = System.currentTimeMillis();
				values.put(
						LegacyDBHelper.NotePad.Notes.COLUMN_NAME_CREATE_DATE,
						now);
				values.put(
						LegacyDBHelper.NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE,
						now);
				values.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_DUE_DATE,
						"");
				values.put(
						LegacyDBHelper.NotePad.Notes.COLUMN_NAME_GTASKS_STATUS,
						"needsAction");

				values.put(LegacyDBHelper.NotePad.Notes.COLUMN_NAME_POSSUBSORT,
						"");
				values.put(
						LegacyDBHelper.NotePad.Notes.COLUMN_NAME_INDENTLEVEL, 0);

				final long noteId = legacyDB.insert(
						LegacyDBHelper.NotePad.Notes.TABLE_NAME, null, values);

				assertTrue("Note insertion should not fail", noteId > 0);
				if (gtasklistid > -1) {
					// Give SOME of the notes google ids
					if (j % 2 == 0) {
						values.clear();
						values.put(
								LegacyDBHelper.NotePad.GTasks.COLUMN_NAME_DB_ID,
								noteId);
						values.put(
								LegacyDBHelper.NotePad.GTasks.COLUMN_NAME_GOOGLE_ACCOUNT,
								anAccount);
						values.put(
								LegacyDBHelper.NotePad.GTasks.COLUMN_NAME_GTASKS_ID,
								anId + j);
						values.put(
								LegacyDBHelper.NotePad.GTasks.COLUMN_NAME_UPDATED,
								aTime);

						final long gtaskid = legacyDB.insert(
								LegacyDBHelper.NotePad.GTasks.TABLE_NAME, null,
								values);
						assertTrue("Gtask insert should not fail", gtaskid > 0);
					}
				}

				// Give all a notification
				values.clear();
				values.put(
						LegacyDBHelper.NotePad.Notifications.COLUMN_NAME_NOTEID,
						noteId);
				values.put(
						LegacyDBHelper.NotePad.Notifications.COLUMN_NAME_TIME,
						System.currentTimeMillis());

				final long notId = legacyDB.insert(
						LegacyDBHelper.NotePad.Notifications.TABLE_NAME, null,
						values);
				assertTrue("legacy notificaiton insert failed", notId > 0);
			}
		}

		legacyDB.setTransactionSuccessful();
		legacyDB.endTransaction();
	}

	@MediumTest
	public void testExistingUpgrade() {
		// First delete test databases if they exist
		context.deleteDatabase(PREFIX + LegacyDBHelper.LEGACY_DATABASE_NAME);
		context.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME);

		final SQLiteDatabase legacyDB = new LegacyDBHelper(context, PREFIX)
				.getWritableDatabase();
		initializeDB(legacyDB);

		// Check that things exist
		Cursor c = DatabaseHandler.getLegacyLists(legacyDB);

		assertEquals("LegacyDB not correct for tests", numOfLegacyLists,
				c.getCount());
		c.close();

		c = DatabaseHandler.getLegacyNotes(legacyDB);
		assertEquals("LegacyDB not correct for tests", numOfLegacyLists
				* numOfLegacyNotes, c.getCount());
		c.close();

		c = DatabaseHandler.getLegacyNotifications(legacyDB);
		assertEquals("LegacyDB not correct for tests", numOfLegacyLists
				* numOfLegacyNotes, c.getCount());
		c.close();

		// Check that new database correctly converts old
		final SQLiteDatabase db = new DatabaseHandler(context, PREFIX).getReadableDatabase();

		c = db.query(TaskList.TABLE_NAME, TaskList.Columns.FIELDS, null, null,
				null, null, null);
		assertEquals("Unexpected amount of lists returned", numOfLegacyLists,
				c.getCount());

		// TODO Examine details
		c.close();

		c = db.query(Task.TABLE_NAME, Task.Columns.FIELDS, null, null, null,
				null, null);
		assertEquals("Incorrect number of notes converted", numOfLegacyLists
				* numOfLegacyNotes, c.getCount());

		// TODO examine details
		c.close();

		c = db.query(Notification.TABLE_NAME, Notification.Columns.FIELDS,
				null, null, null, null, null);
		assertEquals("Incorrect number of notifications converted",
				numOfLegacyLists * numOfLegacyNotes, c.getCount());
		// TODO examine details
		c.close();

		db.close();
		legacyDB.close();

		assertTrue(
				"Could not delete database",
				context.deleteDatabase(PREFIX
						+ LegacyDBHelper.LEGACY_DATABASE_NAME));
		assertTrue("Could not delete database",
				context.deleteDatabase(PREFIX + DatabaseHandler.DATABASE_NAME));
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DaoTaskTest.java
================================================
package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertNotEquals;

import androidx.test.filters.MediumTest;

import com.nononsenseapps.notepad.database.Task;

import junit.framework.TestCase;

public class DaoTaskTest extends TestCase {

	private void copyValsFromTo(final Task task1, final Task task2) {
		task2.title = task1.title;
		task2.note = task1.note;
		task2.completed = task1.completed;
		task2.due = task1.due;
	}

	@MediumTest
	public void testTask() {
		final Task task1 = new Task();
		task1.title = "title1";
		task1.note = "note1";

		// Equals method should be true for these
		assertEquals("Task should equal itself!", task1, task1);

		task1.due = 924592L;
		assertEquals("Task should equal itself!", task1, task1);

		task1.completed = 230456L;
		assertEquals("Task should equal itself!", task1, task1);

		// Create copy
		final Task task2 = new Task();
		copyValsFromTo(task1, task2);

		assertTrue((task1.title != null && task1.title.equals(task2.title)));
		assertTrue((task1.note != null && task1.note.equals(task2.note)));
		assertEquals(task1.due, task2.due);
		assertTrue(((task1.completed != null) == (task2.completed != null)));

		assertEquals("Task1 should equal task2!", task1, task2);

		// Completed should only care about null status
		task2.completed = 9272958113551L;
		assertEquals("Completed should only care about null values", task1, task2);

		// Should all fail
		copyValsFromTo(task1, task2);
		task2.title = "badfa";
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.note = "badfa";
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.due = 29037572395L;
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.completed = null;
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.due = null;
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.title = "badfa";
		task2.note = "asdfal";
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.title = "badfa";
		task2.due = null;
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.note = "badfa";
		task2.due = 292374522222L;
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.title = "badfa";
		task2.note = "asdf";
		task2.due = null;
		assertNotEquals("Task1 should not equal task2!", task1, task2);

		copyValsFromTo(task1, task2);
		task2.title = "badfa";
		task2.note = "asdf";
		task2.due = null;
		task2.completed = null;
		assertNotEquals("Task1 should not equal task2!", task1, task2);
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DashClockSettingsTest.java
================================================
/*
 * Copyright (c) 2014 Jonas Kalderstam.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertNotNull;

import androidx.test.rule.ActivityTestRule;

import com.nononsenseapps.notepad.dashclock.DashclockPrefActivity;

import org.junit.Rule;
import org.junit.Test;

/**
 * Verify that the activity opens OK on any screensize.
 */
public class DashClockSettingsTest {

	// the replacement, ActivityScenarioRule does not work
	@SuppressWarnings("deprecation")
	@Rule
	public final ActivityTestRule<DashclockPrefActivity> mActivityRule
			= new ActivityTestRule<>(DashclockPrefActivity.class, false);

	@Test
	public void testLoadOK() {
		assertNotNull(mActivityRule.getActivity());
		Helper.takeScreenshot("Activity_loaded");
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DateTimeTest.java
================================================
package com.nononsenseapps.notepad.test;

import androidx.test.filters.SmallTest;

import com.nononsenseapps.notepad.database.Task;

import junit.framework.TestCase;

/**
 * Tests related to the Date and Time formats
 */
public class DateTimeTest extends TestCase {

	@Override
	public void setUp() throws Exception {
		super.setUp();
	}

	@Override
	public void tearDown() throws Exception {
		super.tearDown();
	}

	@SmallTest
	public void test_setAsCompleted() {
		Task t = new Task();
		t.setAsCompletedForLegacy(); // <-- see its comments
		assertTrue(t.completed > 0);
	}

}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskDetailTest.java
================================================
package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import androidx.fragment.app.Fragment;

import com.nononsenseapps.notepad.activities.main.ActivityMain_;
import com.nononsenseapps.notepad.espresso_tests.BaseTestClass;
import com.nononsenseapps.notepad.espresso_tests.EspressoHelper;

import org.junit.Test;


public class FragmentTaskDetailTest extends BaseTestClass {

	@Test
	public void testFragmentLoaded() {

		EspressoHelper.hideShowCaseViewIfShown();
		EspressoHelper.createNoteWithName("test note content");

		Fragment fragment = mActRule
				.getActivity()
				.getSupportFragmentManager()
				.findFragmentByTag(ActivityMain_.DETAILTAG);
		assertNotNull("Editor should NOT be null", fragment);
		assertTrue("Editor should be visible",
				fragment.isAdded() && fragment.isVisible());

		Helper.takeScreenshot("Editor_loaded");

	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsTest.java
================================================
package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import android.widget.ListView;

import androidx.fragment.app.Fragment;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;

import com.nononsenseapps.notepad.R;
import com.nononsenseapps.notepad.activities.main.ActivityMain_;

import org.junit.Rule;
import org.junit.Test;

public class FragmentTaskListsTest {

	// the replacement, ActivityScenarioRule does not work
	@SuppressWarnings("deprecation")
	@Rule
	public final ActivityTestRule<ActivityMain_> mActivityRule
			= new ActivityTestRule<>(ActivityMain_.class, false);

	@Test
	public void testSanity() {
		assertEquals("This should succeed", 1, 1);
		assertNotNull("Fragment1-holder should always be present",
				mActivityRule.getActivity().findViewById(R.id.fragment1));
	}

	@Test
	public void testFragmentLoaded() {
		InstrumentationRegistry.getInstrumentation().waitForIdleSync();
		assertNotNull(mActivityRule.getActivity());

		Fragment listPagerFragment = mActivityRule
				.getActivity()
				.getSupportFragmentManager()
				.findFragmentByTag(ActivityMain_.LISTPAGERTAG);

		assertNotNull("List pager fragment should not be null", listPagerFragment);
		assertTrue("List pager fragment should be visible",
				listPagerFragment.isAdded() && listPagerFragment.isVisible());

		ListView taskList = listPagerFragment
				.getView()
				.findViewById(android.R.id.list);

		assertNotNull("Could not find the list!", taskList);

		Helper.takeScreenshot("List_loaded");
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsViewPagerTest.java
================================================
package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import androidx.test.rule.ActivityTestRule;

import com.nononsenseapps.notepad.R;
import com.nononsenseapps.notepad.activities.main.ActivityMain_;

import org.junit.Rule;
import org.junit.Test;

public class FragmentTaskListsViewPagerTest {

	// the replacement, ActivityScenarioRule does not work
	@SuppressWarnings("deprecation")
	@Rule
	public final ActivityTestRule<ActivityMain_> mActivityRule
			= new ActivityTestRule<>(ActivityMain_.class, false);

	@Test
	public void testSanity() {
		assertEquals("This should succeed", 1, 1);
		assertNotNull("Error in the activityrule", mActivityRule.getActivity());
		assertNotNull("Fragment1-holder should always be present",
				mActivityRule.getActivity().findViewById(R.id.fragment1));
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/Helper.java
================================================
package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;

import androidx.test.platform.app.InstrumentationRegistry;

import com.nononsenseapps.notepad.database.Task;
import com.nononsenseapps.notepad.database.TaskList;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class Helper {

	private static Task getATask(final Context context) {
		Cursor c = context
				.getContentResolver()
				.query(Task.URI, Task.Columns.FIELDS, null, null, null);
		Task result = null;
		if (c.moveToFirst())
			result = new Task(c);
		return result;
	}

	private static TaskList getATaskList(final Context context) {
		Cursor c = context
				.getContentResolver()
				.query(TaskList.URI, TaskList.Columns.FIELDS, null, null, null);
		TaskList result = null;
		if (c.moveToFirst())
			result = new TaskList(c);
		return result;
	}

	/**
	 * Takes a screenshots and saves it as "filename", for example
	 * /storage/emulated/0/Android/data/com.nononsenseapps.notepad/files/screenshots/fileName.png
	 * This is mandatory in new android versions, since it's the only folder we can easily write to
	 */
	public static void takeScreenshot(String fileName) {
		// wait a second for the activity to load.
		try {Thread.sleep(1000);} catch (InterruptedException ignored) {}

		var tool = InstrumentationRegistry.getInstrumentation();
		Bitmap bmp = tool.getUiAutomation().takeScreenshot();

		File dir = tool.getTargetContext().getExternalFilesDir("screenshots");
		if (!dir.exists()) {
			assertTrue("Could not create directory", dir.mkdirs());
		}

		// the png file
		var file = new File(dir, fileName + ".png");

		try (var out = new FileOutputStream(file.getAbsolutePath())) {
			bmp.compress(Bitmap.CompressFormat.PNG, 100, out);
		} catch (IOException e) {
			fail("Could not save png screenshot");
		}
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/OrgSyncTest.java
================================================
package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;

import androidx.test.platform.app.InstrumentationRegistry;

import com.nononsenseapps.helpers.FileHelper;
import com.nononsenseapps.notepad.database.RemoteTask;
import com.nononsenseapps.notepad.database.RemoteTaskList;
import com.nononsenseapps.notepad.database.Task;
import com.nononsenseapps.notepad.database.TaskList;
import com.nononsenseapps.notepad.sync.orgsync.OrgConverter;
import com.nononsenseapps.notepad.sync.orgsync.SDSynchronizer;

import org.cowboyprogrammer.org.OrgFile;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;

/**
 * Test the synchronizer code.
 * Methods starting with 'testFresh' are meant to be reused in higher-order
 * tests.
 */
public class OrgSyncTest {

	private static final String ACCOUNT = "bobtester";
	private static String DIR;

	/**
	 * @return a Context to use during testing. It reports the same packagename of the
	 * app: com.nononsenseapps.notepad
	 */
	private static Context getTheContext() {
		// VERY IMPORTANT: it should NOT be .getContext(), because that one uses the namespace
		// com.nononsenseapps.notepad.test which saves the files on a wrong path, which makes
		// every test in this class fail! ApplicationProvider.getApplicationContext() seems fine.
		return InstrumentationRegistry.getInstrumentation().getTargetContext();
	}

	@Before
	public void setUp() {
		DIR = FileHelper.getUserSelectedOrgDir(getTheContext());
		assertNotNull(DIR);
		var d = new File(DIR);
		if (!d.exists()) assertTrue(d.mkdirs());

		// since the app starts with a default, "Welcome!" note, we must clear all notes,
		// (and tasks, org files, ...) to make sure the tests in this class can complete
		tearDown();
	}

	/**
	 * Performs a cleanup of the app's data and org files
	 */
	@After
	public void tearDown() {
		ContentResolver resolver = getTheContext().getContentResolver();
		resolver.delete(TaskList.URI, null, null);
		resolver.delete(Task.URI, null, null);
		resolver.delete(RemoteTaskList.URI, null, null);
		resolver.delete(RemoteTask.URI, null, null);

		File d = new File(DIR);
		File[] filesInFolder = d.listFiles();
		assertNotNull("Can not get files in folder", filesInFolder);
		for (File f : filesInFolder) {
			FileHelper.tryDeleteFile(f, getTheContext());
		}
	}

	public ArrayList<TaskList> getTaskLists() {
		ContentResolver resolver = getTheContext().getContentResolver();
		Cursor c = resolver.query(TaskList.URI, TaskList.Columns
				.FIELDS, null, null, null);

		ArrayList<TaskList> result = new ArrayList<>();
		while (c.moveToNext()) {
			result.add(new TaskList(c));
		}
		c.close();

		return result;
	}

	public ArrayList<Task> getTasks(final long listid) {
		ContentResolver resolver = getTheContext().getContentResolver();
		Cursor c = resolver.query(Task.URI, Task.Columns
						.FIELDS, Task.Columns.DBLIST + " IS ?",
				new String[] { Long.toString(listid) }, null
		);

		ArrayList<Task> result = new ArrayList<>();
		while (c.moveToNext()) {
			result.add(new Task(c));
		}
		c.close();

		return result;
	}

	public ArrayList<RemoteTaskList> getRemoteTaskLists() {
		ContentResolver resolver = getTheContext().getContentResolver();
		Cursor c = resolver.query(RemoteTaskList.URI, RemoteTaskList.Columns
						.FIELDS, RemoteTaskList.Columns.ACCOUNT + " IS ?",
				new String[] { ACCOUNT }, null
		);

		ArrayList<RemoteTaskList> result = new ArrayList<>();
		while (c.moveToNext()) {
			result.add(new RemoteTaskList(c));
		}
		c.close();

		return result;
	}

	public ArrayList<RemoteTask> getRemoteTasks() {
		ContentResolver resolver = getTheContext().getContentResolver();
		Cursor c = resolver.query(RemoteTask.URI, RemoteTask.Columns
						.FIELDS, RemoteTask.Columns.ACCOUNT + " IS ?",
				new String[] { ACCOUNT }, null
		);

		ArrayList<RemoteTask> result = new ArrayList<>();
		while (c.moveToNext()) {
			result.add(new RemoteTask(c));
		}
		c.close();

		return result;
	}

	@Test
	public void testPass() {
		// This always passes
		assertTrue(true);
	}

	@Test
	public void testTester() {
		TestSynchronizer tester = new TestSynchronizer(getTheContext());
		assertTrue(tester.isConfigured());
	}

	/**
	 * End result: synced state of one tasklist with two tasks.
	 * Tested flow branches:
	 * - Lists: Create file
	 * - Tasks: Create node
	 */
	@Test
	public void testFreshSimple() {
		// First create a list with 2 tasks
		TaskList list = new TaskList();
		list.title = "TestList";
		list.save(getTheContext());
		assertTrue(list._id > 0);

		final int taskCount = 2;
		for (int i = 0; i < taskCount; i++) {
			Task t = new Task();
			t.dblist = list._id;
			t.title = "Task" + i;
			t.note = "A body for the task";
			t.save(getTheContext());
			assertTrue(t._id > 0);
		}

		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());

		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// See the result
		HashSet<String> filenames = synchronizer.getRemoteFilenames();

		assertEquals("Only one list was created.", 1, filenames.size());

		String filename = null;
		for (String f : filenames) {
			filename = f;
		}

		assertEquals("Wrong filename", list.title + ".org", filename);

		// Check that the database is correct
		ArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();
		assertEquals("Should only be one RemoteList!", 1, remoteLists.size());

		ArrayList<RemoteTask> remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly 2 RemoteTasks", taskCount, remoteTasks.size());
		long lastDbid = -1;
		for (int i = 1; i < remoteTasks.size() + 1; i++) {
			RemoteTask r = remoteTasks.remove(i - 1);
			// Check for duplicates
			assertEquals("Id is not correct", i, r._id);
			assertTrue(lastDbid != r.dbid);
			lastDbid = r.dbid;
		}
	}

	public void syncAndAssertNothingChanged(final int taskCount) {
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// It should NOT have written to disk at all
		assertEquals("No changes should not be written!", 0,
				synchronizer.getPutRemoteCount());

		// Check that the database is still correct
		ArrayList<TaskList> lists = getTaskLists();
		assertEquals("Should only be one list", 1, lists.size());

		ArrayList<Task> tasks = getTasks(lists.get(0)._id);
		assertEquals("Should be only 2 tasks in list", taskCount, tasks.size());

		ArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();
		assertEquals("Should only be one RemoteList!", 1, remoteLists.size());

		ArrayList<RemoteTask> remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly 2 RemoteTasks", taskCount, remoteTasks.size());
		long lastDbid = -1;
		for (int i = 1; i < remoteTasks.size() + 1; i++) {
			RemoteTask r = remoteTasks.get(i - 1);
			// Check for duplicates
			assertEquals("Id is not correct", i, r._id);
			assertTrue(lastDbid != r.dbid);
			lastDbid = r.dbid;
		}
	}

	/**
	 * Nothing has changed here.
	 * Tested flow branches:
	 * - Lists: Update Merge
	 * - Tasks: Update Merge
	 */
	@Test
	public void testNothingNew() {
		final int taskCount = 2;
		testFreshSimple();

		syncAndAssertNothingChanged(taskCount);
		syncAndAssertNothingChanged(taskCount);
		syncAndAssertNothingChanged(taskCount);
		syncAndAssertNothingChanged(taskCount);
	}

	/**
	 * Having two lists with the same name is possible in the app,
	 * but obviously impossible at the filesystem level.
	 * Tested flow branches:
	 * - Lists: Create file
	 */
	@Test
	public void testDuplicateName() {
		// Create first list
		TaskList list1 = new TaskList();
		list1.title = "TestList";
		list1.save(getTheContext());
		assertTrue(list1._id > 0);

		// Create second list
		TaskList list2 = new TaskList();
		list2.title = "TestList";
		list2.save(getTheContext());
		assertTrue(list2._id > 0);

		// Sync it
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Make sure the second one was renamed!
		for (TaskList tl : getTaskLists()) {
			if (tl._id == list1._id) {
				assertEquals(list1.title, tl.title);
			} else if (tl._id == list2._id) {
				assertEquals("List should have been renamed",
						list2.title + 1, tl.title);
			}
		}

		HashSet<String> filenames = synchronizer.getRemoteFilenames();
		assertEquals(2, filenames.size());
		assertTrue(filenames.contains(list1.title + ".org"));
		assertTrue(filenames.contains(list2.title + 1 + ".org"));
	}

	/**
	 * Renaming a list in the app should rename the file.
	 * Tested branches:
	 * - Update list, renamed
	 */
	@Test
	public void testRenamedList() {
		// Create first list
		TaskList list1 = new TaskList();
		list1.title = "TestList";
		list1.save(getTheContext());
		assertTrue(list1._id > 0);

		// Sync it
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		File org = new File(DIR, OrgConverter.getTitleAsFilename
				(list1));
		// Make sure original file is there
		assertTrue(org.exists());

		// Rename the list
		list1.title = "RenamedList";
		list1.save(getTheContext());

		// Sync it
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Make sure rename was successful
		assertFalse(org.exists());

		File renamed = new File(DIR, OrgConverter.getTitleAsFilename
				(list1));
		assertTrue(renamed.exists());
	}

	/**
	 * Deleting a list should delete the corresponding file and all tasks.
	 * Tested branches:
	 * - Delete File Db
	 */
	@Test
	public void testDeletedList() {
		// Setup simple DB
		final int taskCount = 2;
		testFreshSimple();

		// Delete list(s)
		File file = null;
		ArrayList<TaskList> lists = getTaskLists();
		for (TaskList list : lists) {
			file = new File(DIR, OrgConverter.getTitleAsFilename(list));

			list.delete(getTheContext());
		}

		assertNotNull(file);
		// Make sure it exists at this point
		assertTrue(file.exists());
		// And that the database still has a record of it
		ArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();
		assertEquals("Should be one RemoteList!", 1, remoteLists.size());

		ArrayList<RemoteTask> remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly 2 RemoteTasks", taskCount, remoteTasks.size());

		// Sync it again
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check that the database has removed it
		lists = getTaskLists();
		assertTrue("Should be no list", lists.isEmpty());

		remoteLists = getRemoteTaskLists();
		assertTrue("Should be no RemoteList!", remoteLists.isEmpty());

		remoteTasks = getRemoteTasks();
		assertTrue("Should be no RemoteTasks", remoteTasks.isEmpty());

		// Make sure no file exists anymore
		assertFalse(file.exists());
	}

	/**
	 * Test moving 1 task from List A to List B
	 */
	@Test
	public void testMoveOne() {
		// First create Two lists
		TaskList listA = new TaskList();
		listA.title = "TestListA";
		listA.save(getTheContext());
		assertTrue(listA._id > 0);

		TaskList listB = new TaskList();
		listB.title = "TestListB";
		listB.save(getTheContext());
		assertTrue(listB._id > 0);

		// Add one task in ListA
		Task t = new Task();
		t.dblist = listA._id;
		t.title = "Task";
		t.note = "A body for the task";
		t.save(getTheContext());
		assertTrue(t._id > 0);

		// Sync it
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());

		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check state of sync
		ArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();
		assertEquals("Should be two RemoteLists!", 2, remoteLists.size());

		ArrayList<RemoteTask> remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly 1 RemoteTask", 1, remoteTasks.size());

		assertEquals("RemoteTask is in wrong list!", listA._id,
				(long) remoteTasks.get(0).listdbid);

		// Move the task
		t.dblist = listB._id;
		t.save(getTheContext());

		// Trigger should have deleted remotes now
		remoteTasks = getRemoteTasks();
		for (RemoteTask rt : remoteTasks) {
			assertEquals("RemoteTask should be deleted after move before sync", "deleted", rt.deleted);
		}

		// Sync it
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check state of sync
		remoteLists = getRemoteTaskLists();
		assertEquals("Should be two RemoteLists after move!", 2, remoteLists.size());

		remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly 1 RemoteTask after move", 1, remoteTasks.size());

		assertEquals("RemoteTask is in wrong list after move!", listB._id,
				(long) remoteTasks.get(0).listdbid);
	}

	/**
	 * Test moving 20 tasks from List A to List B
	 */
	@Test
	public void testMoveMany() {
		// First create Two lists
		TaskList listA = new TaskList();
		listA.title = "TestListA";
		listA.save(getTheContext());
		assertTrue(listA._id > 0);

		TaskList listB = new TaskList();
		listB.title = "TestListB";
		listB.save(getTheContext());
		assertTrue(listB._id > 0);

		final int taskCount = 20;
		ArrayList<Task> tasks = new ArrayList<>();
		for (int i = 0; i < taskCount; i++) {
			Task t = new Task();
			t.dblist = listA._id;
			t.title = "Task" + i;
			t.note = "A body for the task";
			t.save(getTheContext());
			assertTrue(t._id > 0);

			tasks.add(t);
		}

		// Sync it
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());

		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check state of sync
		ArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();
		assertEquals("Should be two RemoteLists!", 2, remoteLists.size());

		ArrayList<RemoteTask> remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly x RemoteTask", taskCount, remoteTasks.size());

		for (RemoteTask remoteTask : remoteTasks) {
			assertEquals("RemoteTask is in wrong list!", listA._id,
					(long) remoteTask.listdbid);
		}

		// Move the tasks
		for (Task t : tasks) {
			t.dblist = listB._id;
			t.save(getTheContext());
		}

		// Trigger should have deleted remotes now
		remoteTasks = getRemoteTasks();
		for (RemoteTask rt : remoteTasks) {
			assertEquals("RemoteTask should be deleted after move before sync", "deleted", rt.deleted);
		}

		// Sync it
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check state of sync
		remoteLists = getRemoteTaskLists();
		assertEquals("Should be two RemoteLists after move!", 2, remoteLists.size());

		remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly x RemoteTask after move and sync", taskCount, remoteTasks.size());

		for (RemoteTask remoteTask : remoteTasks) {
			assertEquals("RemoteTask is in wrong list after move!", listB._id,
					(long) remoteTask.listdbid);
		}
	}

	/**
	 * Test moving 12 tasks from List A to List B where there are 12 lists each with 20 tasks
	 */
	@Test
	public void testMoveManyAmongMany() {
		final int listCount = 12;
		final int taskCount = 20;
		final int movedTaskCount = 12;
		ArrayList<Task> tasksToMove = new ArrayList<>();
		TaskList listA = null, listB = null;
		// First create Lists
		for (int listIndex = 0; listIndex < listCount; listIndex++) {
			TaskList list = new TaskList();
			list.title = "TestList" + listIndex;
			list.save(getTheContext());
			assertTrue(list._id > 0);

			if (listA == null)
				listA = list;
			else if (listB == null)
				listB = list;

			for (int i = 0; i < taskCount; i++) {
				Task t = new Task();
				t.dblist = list._id;
				t.title = "Task" + listIndex + "." + i;
				t.note = "A body for the task";
				t.save(getTheContext());
				assertTrue(t._id > 0);

				if (tasksToMove.size() < movedTaskCount) {
					tasksToMove.add(t);
				}
			}
		}

		// Sync it
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());

		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check state of sync
		ArrayList<RemoteTaskList> remoteLists = getRemoteTaskLists();
		assertEquals("Should be X RemoteLists!", listCount, remoteLists.size());

		ArrayList<RemoteTask> remoteTasks = getRemoteTasks();
		assertEquals("Should be exactly x RemoteTask", taskCount * listCount, remoteTasks.size());

		// Move the tasks
		assertNotNull(listA);
		assertNotNull(listB);
		assertTrue("List A and B should be different!", listA._id != listB._id);
		assertEquals("Expected something to move", movedTaskCount, tasksToMove.size());
		for (Task t : tasksToMove) {
			assertEquals("Expected task to be in list A!", listA._id, (long) t.dblist);
			t.dblist = listB._id;
			t.save(getTheContext());
		}

		// Trigger should have deleted remotes now
		remoteTasks = getRemoteTasks();
		int deletecount = 0;
		int realcount = 0;
		for (RemoteTask rt : remoteTasks) {
			if ("deleted".equals(rt.deleted)) {
				deletecount += 1;
			} else {
				realcount += 1;
			}
		}

		assertEquals("Deleted remotetasks did not match", movedTaskCount, deletecount);
		assertEquals("Remaining remotetasks did not match", taskCount * listCount - movedTaskCount, realcount);

		// Sync it
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check state of sync
		ArrayList<RemoteTaskList> remoteTaskLists = getRemoteTaskLists();
		remoteTasks = getRemoteTasks();
		deletecount = 0;
		realcount = 0;
		for (RemoteTask rt : remoteTasks) {
			if ("deleted".equals(rt.deleted)) {
				deletecount += 1;
			} else {
				realcount += 1;
			}
		}

		assertEquals("Number of remote lits did not match", listCount, remoteTaskLists.size());
		assertEquals("Deleted remotetasks did not match", 0, deletecount);
		assertEquals("Remaining remotetasks did not match", taskCount * listCount, realcount);

		int nowInB = 0;
		for (RemoteTask remoteTask : remoteTasks) {
			assertNotEquals("deleted", remoteTask.deleted);
			if (remoteTask.listdbid == listB._id) {
				nowInB += 1;
			}
		}
		assertEquals("RemoteTasks in b not expected count", taskCount + movedTaskCount,
				nowInB);

		// Check same things for local tasks
		ArrayList<TaskList> taskLists = getTaskLists();
		assertEquals("Number of lists did not match", listCount, taskLists.size());
		for (TaskList list : taskLists) {
			ArrayList<Task> tasks = getTasks(list._id);
			if (listA._id == list._id) {
				assertEquals("Not expected count in A", taskCount - movedTaskCount, tasks.size());
			} else if (listB._id == list._id) {
				assertEquals("Not expected count in B", taskCount + movedTaskCount, tasks.size());
			} else {
				assertEquals("Not expected count in C->", taskCount, tasks.size());
			}
		}
	}

	@Test
	public void testFilenameWithSlash() {
		// Filenames with slashes are not permitted
		final TaskList lista = new TaskList();
		lista.title = "Test/List/Slash/Name";
		lista.save(getTheContext());
		assertTrue(lista._id > 0);

		// Sync it
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());

		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check contents after sync
		final TaskList listb = getTaskLists().get(0);

		// Should no longer have slashes in name
		assertFalse(listb.title.contains("/"));
		assertEquals("Test_List_Slash_Name", listb.title);
	}

	@Test
	public void testContentStability() {
		// Make sure content is not changed
		// Create list
		final TaskList lista = new TaskList();
		lista.title = "TestList";
		lista.save(getTheContext());
		assertTrue(lista._id > 0);

		final Task task1a = new Task();
		task1a.title = "The title1";
		task1a.note = "A note without newline";
		task1a.dblist = lista._id;
		task1a.save(getTheContext());
		assertTrue(task1a._id > 0);

		final Task task2a = new Task();
		task2a.title = "The title2";
		task2a.note = "Another note\non two lines";
		task2a.dblist = lista._id;
		task2a.save(getTheContext());
		assertTrue(task2a._id > 0);

		// Sync it
		TestSynchronizer synchronizer = new TestSynchronizer(getTheContext());

		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check contents after sync
		final TaskList listb = getTaskLists().get(0);

		assertEquals(lista.title, listb.title);


		for (Task taskb : getTasks(listb._id)) {
			Task org;
			if (taskb._id == task1a._id) {
				org = task1a;
			} else {
				org = task2a;
			}
			// Compare title and note
			assertEquals(org.title, taskb.title);
			assertEquals(org.note, taskb.note);
		}

		// Sync it again
		try {
			synchronizer.fullSync();
		} catch (Exception e) {
			fail(e.getLocalizedMessage());
		}

		// Check contents after sync
		final TaskList listc = getTaskLists().get(0);

		assertEquals(lista.title, listc.title);

		for (Task taskc : getTasks(listb._id)) {
			Task org;
			if (taskc._id == task1a._id) {
				org = task1a;
			} else {
				org = task2a;
			}
			// Compare title and note
			assertEquals(org.title, taskc.title);
			assertEquals(org.note, taskc.note);
		}
	}

	static class TestSynchronizer extends SDSynchronizer {

		private int putRemoteCount = 0;

		public TestSynchronizer(Context context) {
			super(context);
		}

		@Override
		public boolean isConfigured() {
			return true;
		}

		/**
		 * @return A unique name for this service. Should be descriptive, like
		 * SDOrg or SSHOrg.
		 */
		@Override
		public String getServiceName() {
			return ACCOUNT;
		}

		@Override
		public String getAccountName() {
			return ACCOUNT;
		}

		/**
		 * Replaces the file on the remote end with the given content.
		 *
		 * @param orgFile The file to save. Uses the filename stored in the object.
		 */
		@Override
		public void putRemoteFile(OrgFile orgFile) throws IOException {
			putRemoteCount += 1;
			super.putRemoteFile(orgFile);
		}

		public int getPutRemoteCount() {
			return putRemoteCount;
		}

		public void setPutRemoteCount(final int putRemoteCount) {
			this.putRemoteCount = putRemoteCount;
		}
	}
}


================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/ProviderHelperTest.java
================================================
/*
 * Copyright (c) 2015 Jonas Kalderstam.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.nononsenseapps.notepad.test;

import static org.junit.Assert.assertEquals;

import com.nononsenseapps.notepad.android.provider.ProviderHelper;

import org.junit.Test;

public class ProviderHelperTest {

	@Test
	public void testGetRelativePath() throws Exception {
		assertEquals("/foo/bar",
				ProviderHelper.getRelativePath("/ACTION/foo/bar"));
		assertEquals("/foo/bar",
				ProviderHelper.getRelativePath("ACTION/foo/bar"));
		assertEquals("/",
				ProviderHelper.getRelativePath("/Action"));
		assertEquals("/",
				ProviderHelper.getRelativePath("Action"));
	}

	@Test
	public void testFirstPart() throws Exception {
		assertEquals("", ProviderHelper.firstPart(""));
		assertEquals("foo", ProviderHelper.firstPart("foo"));
		assertEquals("foo", ProviderHelper.firstPart("/foo"));
		assertEquals("foo", ProviderHelper.firstPart("/foo/bar"));
		assertEquals("foo", ProviderHelper.firstPart("foo/bar"));
	}

	@Test
	public void testRestPart() throws Exception {
		assertEquals("", ProviderHelper.restPart("foo"));
		assertEquals("", ProviderHelper.restPart("/foo"));
		assertEquals("", ProviderHelper.restPart("/foo/"));
		assertEquals("bar/baz", ProviderHelper.restPart("/foo/bar/baz"));
		assertEquals("bar/baz", ProviderHelper.restPart("foo/bar/baz"));
		assertEquals("bar/baz", ProviderHelper.restPart("/foo/bar/baz"));
	}

	@Test
	public void testMatchPath() throws Exception {
		// Root cases
		assertEquals(ProviderHelper.URI_ROOT,
				ProviderHelper.matchPath(null));
		assertEquals(ProviderHelper.URI_ROOT,
				ProviderHelper.matchPath(""));
		assertEquals(ProviderHelper.URI_ROOT,
				ProviderHelper.matchPath("/"));

		// List
		assertEquals(ProviderHelper.URI_LIST,
				ProviderHelper.matchPath("/list"));
		assertEquals(ProviderHelper.URI_LIST,
				ProviderHelper.matchPath("/list/"));
		assertEquals(ProviderHelper.URI_LIST,
				ProviderHelper.matchPath("list/"));
		assertEquals(ProviderHelper.URI_LIST,
				ProviderHelper.matchPath("/list/"));

		assertEquals(ProviderHelper.URI_LIST,
				ProviderHelper.matchPath("/list/foo"));
		assertEquals(ProviderHelper.URI_LIST,
				ProviderHelper.matchPath("list/foo/bar/"));

		// Details
		assertEquals(ProviderHelper.URI_DETAILS,
				ProviderHelper.matchPath("/details/foo"));
		assertEquals(ProviderHelper.URI_DETAILS,
				ProviderHelper.matchPath("details/foo/bar/"));

		// These uris are invalid
		assertEquals(ProviderHelper.URI_NOMATCH,
				ProviderHelper.matchPath("details"));

		assertEquals(ProviderHelper.URI_NOMATCH,
				ProviderHelper.matchPath("details/"));

		assertEquals(ProviderHelper.URI_NOMATCH,
				ProviderHelper.matchPath("unknownpredicate/foo/bar"));
	}

	@Test
	public void testJoin() throws Exception {
		assertEquals("/foo/bar", ProviderHelper.join("/foo", "bar"));
		assertEquals("/foo/bar", ProviderHelper.join("/foo", "/bar"));
		assertEquals("/foo/bar", ProviderHelper.join("/foo/", "/bar"));
		assertEquals("/foo/bar", ProviderHelper.join("/foo/", "bar"));
		assertEquals("/", ProviderHelper.join("/", "/"));
		assertEquals("/", ProviderHelper.join("/", ""));
		assertEquals("/", ProviderHelper.join("", "/"));
	}
}

================================================
FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/RFCDateTest.java
================================================
package com.nononsenseapps.notepad.test;

import android.util.Log;

import com.nononsenseapps.helpers.RFC3339Date;

import junit.framework.TestCase;

import java.util.Calendar;

public class RFCDateTest extends TestCase {

	static final String TAG = "nononsenseapps rfctest";

	// Sun May  5 23:53:10 2013
	//static final long atime = 1367790790000L;
	// 2 Hours in milli seconds, for MY TIMEZONE
	//static final long twohours = 7200000L;

//	public void testCalendar() {
//		Calendar c = Calendar.getInstance();
//		c.setTimeInMillis(RFC3339Date.localAsRFC3339(atime));
//		
//		assertEquals("GMT would show as 21:53", 21, c.get(Calendar.HOUR_OF_DAY));
//		
//		//Log.d(TAG, "gtinm: " + c.getTimeInMillis() + ", gtgt: " + c.getTime().getTime());
//		//assertTrue("Is wrong time", c.getTimeInMillis() == c.getTime().getTime());
//	}

//	public void testUTCFUCKSHIT() throws IndexOutOfBoundsException, ParseException {
//		Log.d(TAG, "Start");
//		Log.d(TAG, RFC3339Date.localAsRFC3339(atim
Download .txt
gitextract_hq_i4sxh/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── notifications_do_not_appear.md
│   └── workflows/
│       ├── android_build.yml
│       ├── android_tests.yml
│       └── publish_playstore.yml
├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── build.gradle
│   ├── dbgenout/
│   │   └── com/
│   │       └── nononsenseapps/
│   │           └── notepad/
│   │               └── database/
│   │                   ├── DBItem.java
│   │                   ├── DatabaseHandler.java
│   │                   ├── DatabaseTriggers.java
│   │                   ├── DatabaseViews.java
│   │                   ├── ItemProvider.java
│   │                   └── taskviewItem.java
│   ├── dbsetup.py
│   ├── proguard.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── nononsenseapps/
│       │               └── notepad/
│       │                   ├── espresso_tests/
│       │                   │   ├── BaseTestClass.java
│       │                   │   ├── EspressoHelper.java
│       │                   │   ├── TestAddBigNumberOfNotesScrollDownAndDeleteOne.java
│       │                   │   ├── TestAddNewNoteShouldShowNameInNotesScreen.java
│       │                   │   ├── TestAddNewNoteWithDueDateCheckDateIsVisible.java
│       │                   │   ├── TestAddNewNoteWithReminderDateAndTime.java
│       │                   │   ├── TestAddNoteToTaskList.java
│       │                   │   ├── TestAddNotesAndRotateScreen.java
│       │                   │   ├── TestAddTaskListAndRotateScreen.java
│       │                   │   ├── TestAddTaskListCheckItIsAddedToDrawer.java
│       │                   │   ├── TestAddTaskListsScrollNavigationDrawer.java
│       │                   │   ├── TestCompletedTasksAreCleared.java
│       │                   │   ├── TestCreateNoteAndDeleteIt.java
│       │                   │   ├── TestCreateTaskListAndDeleteIt.java
│       │                   │   ├── TestPasswords.java
│       │                   │   └── TestSaveLoadJsonBackup.java
│       │                   └── test/
│       │                       ├── DBFreshTest.java
│       │                       ├── DBProviderMovementTest.java
│       │                       ├── DBProviderTest.java
│       │                       ├── DBUpgradeTest.java
│       │                       ├── DaoTaskTest.java
│       │                       ├── DashClockSettingsTest.java
│       │                       ├── DateTimeTest.java
│       │                       ├── FragmentTaskDetailTest.java
│       │                       ├── FragmentTaskListsTest.java
│       │                       ├── FragmentTaskListsViewPagerTest.java
│       │                       ├── Helper.java
│       │                       ├── OrgSyncTest.java
│       │                       ├── ProviderHelperTest.java
│       │                       ├── RFCDateTest.java
│       │                       └── StorageTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── .gitignore
│           ├── java/
│           │   └── com/
│           │       ├── google/
│           │       │   └── android/
│           │       │       └── apps/
│           │       │           └── dashclock/
│           │       │               └── ui/
│           │       │                   └── DragGripView.java
│           │       └── nononsenseapps/
│           │           ├── helpers/
│           │           │   ├── ActivityHelper.java
│           │           │   ├── DocumentFileHelper.java
│           │           │   ├── FileHelper.java
│           │           │   ├── FilePickerHelper.java
│           │           │   ├── ListHelper.java
│           │           │   ├── NnnLogger.java
│           │           │   ├── NotificationHelper.java
│           │           │   ├── PermissionsHelper.java
│           │           │   ├── PreferencesHelper.java
│           │           │   ├── RFC3339Date.java
│           │           │   ├── SyncStatusMonitor.java
│           │           │   ├── ThemeHelper.java
│           │           │   ├── TimeFormatter.java
│           │           │   └── UpdateNotifier.java
│           │           ├── notepad/
│           │           │   ├── NnnApp.java
│           │           │   ├── NotePadBroadcastReceiver.java
│           │           │   ├── activities/
│           │           │   │   ├── ActivitySearch.java
│           │           │   │   ├── ActivitySearchDeleted.java
│           │           │   │   ├── ActivityTaskHistory.java
│           │           │   │   └── main/
│           │           │   │       ├── ActivityMain.java
│           │           │   │       ├── ActivityMainHelper.java
│           │           │   │       └── DrawerCursorLoader.java
│           │           │   ├── android/
│           │           │   │   └── provider/
│           │           │   │       ├── DummyProvider.java
│           │           │   │       ├── ProviderHelper.java
│           │           │   │       ├── ProviderManager.java
│           │           │   │       └── TextFileProvider.java
│           │           │   ├── dashclock/
│           │           │   │   ├── DashclockPrefActivity.java
│           │           │   │   ├── DashclockPrefsFragment.java
│           │           │   │   └── TasksExtension.java
│           │           │   ├── database/
│           │           │   │   ├── DAO.java
│           │           │   │   ├── DatabaseHandler.java
│           │           │   │   ├── LegacyDBHelper.java
│           │           │   │   ├── MyContentProvider.java
│           │           │   │   ├── Notification.java
│           │           │   │   ├── RemoteTask.java
│           │           │   │   ├── RemoteTaskList.java
│           │           │   │   ├── Task.java
│           │           │   │   └── TaskList.java
│           │           │   ├── fragments/
│           │           │   │   ├── DialogConfirmBase.java
│           │           │   │   ├── DialogConfirmBaseV11.java
│           │           │   │   ├── DialogDeleteCompletedTasks.java
│           │           │   │   ├── DialogDeleteList.java
│           │           │   │   ├── DialogDeleteTask.java
│           │           │   │   ├── DialogEditList.java
│           │           │   │   ├── DialogExportBackup.java
│           │           │   │   ├── DialogMoveToList.java
│           │           │   │   ├── DialogPassword.java
│           │           │   │   ├── DialogPasswordV11.java
│           │           │   │   ├── DialogRestore.java
│           │           │   │   ├── DialogRestoreBackup.java
│           │           │   │   ├── FragmentSearch.java
│           │           │   │   ├── FragmentSearchDeleted.java
│           │           │   │   ├── TaskDetailFragment.java
│           │           │   │   ├── TaskListFragment.java
│           │           │   │   └── TaskListViewPagerFragment.java
│           │           │   ├── interfaces/
│           │           │   │   ├── ListOpener.java
│           │           │   │   ├── MenuStateController.java
│           │           │   │   └── OnFragmentInteractionListener.java
│           │           │   ├── prefs/
│           │           │   │   ├── AboutPrefs.java
│           │           │   │   ├── AppearancePrefs.java
│           │           │   │   ├── BackupPrefs.java
│           │           │   │   ├── Constants.java
│           │           │   │   ├── IndexPrefs.java
│           │           │   │   ├── ListPrefs.java
│           │           │   │   ├── NotificationPrefs.java
│           │           │   │   ├── PasswordPrefs.java
│           │           │   │   ├── PrefsActivity.java
│           │           │   │   └── SyncPrefs.java
│           │           │   ├── sync/
│           │           │   │   ├── SyncAdapter.java
│           │           │   │   ├── files/
│           │           │   │   │   └── JSONBackup.java
│           │           │   │   ├── googleapi/
│           │           │   │   │   ├── GoogleTask.java
│           │           │   │   │   └── GoogleTaskList.java
│           │           │   │   └── orgsync/
│           │           │   │       ├── BackgroundSyncScheduler.java
│           │           │   │       ├── DBSyncBase.java
│           │           │   │       ├── Monitor.java
│           │           │   │       ├── OrgConverter.java
│           │           │   │       ├── OrgProvider.java
│           │           │   │       ├── OrgSyncService.java
│           │           │   │       ├── RemoteTaskListFile.java
│           │           │   │       ├── RemoteTaskNode.java
│           │           │   │       ├── SDSynchronizer.java
│           │           │   │       ├── Synchronizer.java
│           │           │   │       └── SynchronizerInterface.java
│           │           │   └── widget/
│           │           │       ├── list/
│           │           │       │   ├── ListWidgetConfig.java
│           │           │       │   ├── ListWidgetProvider.java
│           │           │       │   ├── ListWidgetService.java
│           │           │       │   └── WidgetPrefs.java
│           │           │       └── shortcut/
│           │           │           └── ShortcutConfig.java
│           │           └── ui/
│           │               ├── DateView.java
│           │               ├── DelegateFrame.java
│           │               ├── ExtraTypesCursorAdapter.java
│           │               ├── ExtrasCursorAdapter.java
│           │               ├── GreyableToggleButton.java
│           │               ├── NoteCheckBox.java
│           │               ├── NotificationItemHelper.java
│           │               ├── ShowcaseHelper.java
│           │               ├── StyledEditText.java
│           │               ├── TitleNoteTextView.java
│           │               ├── ViewsHelper.java
│           │               └── WeekDaysView.java
│           └── res/
│               ├── anim/
│               │   ├── activity_slide_in_right.xml
│               │   ├── activity_slide_out_right_full.xml
│               │   ├── cycle_7.xml
│               │   ├── shake.xml
│               │   ├── slide_in_bottom.xml
│               │   ├── slide_in_top.xml
│               │   ├── slide_out_bottom.xml
│               │   └── slide_out_top.xml
│               ├── drawable/
│               │   ├── btn_toggle_bg.xml
│               │   ├── folder_move_24dp.xml
│               │   ├── folder_plus_24dp.xml
│               │   ├── ic_add_24dp.xml
│               │   ├── ic_alarm_24dp.xml
│               │   ├── ic_alarm_add_24dp.xml
│               │   ├── ic_archive_24dp.xml
│               │   ├── ic_brightness_6_24dp.xml
│               │   ├── ic_check_24dp.xml
│               │   ├── ic_checkbox_checked.xml
│               │   ├── ic_checkbox_unchecked.xml
│               │   ├── ic_clear_24dp.xml
│               │   ├── ic_copy_24dp.xml
│               │   ├── ic_delete_24dp.xml
│               │   ├── ic_export.xml
│               │   ├── ic_folder_24dp.xml
│               │   ├── ic_help_24dp.xml
│               │   ├── ic_import.xml
│               │   ├── ic_info_24dp.xml
│               │   ├── ic_lock_closed_24dp.xml
│               │   ├── ic_lock_open_24dp.xml
│               │   ├── ic_notebook_minus_24dp.xml
│               │   ├── ic_refresh_24dp.xml
│               │   ├── ic_sd_storage_24dp.xml
│               │   ├── ic_search_24dp.xml
│               │   ├── ic_select_all.xml
│               │   ├── ic_settings_24dp.xml
│               │   ├── ic_share_24dp.xml
│               │   ├── ic_sort_24dp.xml
│               │   ├── ic_stat_notification_edit.xml
│               │   ├── ic_underline_accent.xml
│               │   ├── ic_undo_24dp.xml
│               │   ├── img_default_selector_dark.xml
│               │   ├── img_default_selector_light.xml
│               │   ├── tasklist_item_blackclassic_bg.xml
│               │   ├── tasklist_item_darkcard_bg.xml
│               │   ├── tasklist_item_lightcard_bg.xml
│               │   └── tasklist_item_lightclassic_bg.xml
│               ├── drawable-anydpi-v26/
│               │   ├── app_icon.xml
│               │   ├── icon_foreground_symbol.xml
│               │   ├── icon_gradient.xml
│               │   └── icon_monochrome.xml
│               ├── layout/
│               │   ├── actionbar_custom_view_done.xml
│               │   ├── actionbar_custom_view_done_discard.xml
│               │   ├── actionbar_discard_button.xml
│               │   ├── actionbar_done_button.xml
│               │   ├── activity_dashclock_settings.xml
│               │   ├── activity_main.xml
│               │   ├── activity_settings.xml
│               │   ├── activity_shortcut_config.xml
│               │   ├── activity_task_history.xml
│               │   ├── activity_widget_config.xml
│               │   ├── activity_widget_config_part_preview.xml
│               │   ├── activity_widget_config_part_settings.xml
│               │   ├── app_pref_about_layout.xml
│               │   ├── app_pref_password_layout.xml
│               │   ├── dialog_ok_cancel.xml
│               │   ├── drawer_header.xml
│               │   ├── drawer_layout.xml
│               │   ├── fragment_dialog_editlist.xml
│               │   ├── fragment_dialog_movetolist.xml
│               │   ├── fragment_dialog_password.xml
│               │   ├── fragment_dialog_restore.xml
│               │   ├── fragment_search.xml
│               │   ├── fragment_task_detail.xml
│               │   ├── fragment_task_list.xml
│               │   ├── fragment_tasklist_viewpager.xml
│               │   ├── fullscreen_fragment.xml
│               │   ├── notification_view.xml
│               │   ├── simple_light_list_item_activated_1.xml
│               │   ├── simple_listitem.xml
│               │   ├── spinner_item.xml
│               │   ├── tasklist_header.xml
│               │   ├── tasklist_item_card_section.xml
│               │   ├── tasklist_item_rich.xml
│               │   ├── weekdays_layout.xml
│               │   ├── widget_layout.xml
│               │   ├── widgetlist_header.xml
│               │   └── widgetlist_item.xml
│               ├── layout-sw600dp-land/
│               │   ├── activity_main.xml
│               │   └── activity_settings.xml
│               ├── menu/
│               │   ├── activity_deleted_context.xml
│               │   ├── activity_main.xml
│               │   ├── fragment_search.xml
│               │   ├── fragment_tasklist.xml
│               │   ├── fragment_tasklist_context.xml
│               │   ├── fragment_tasklists_viewpager.xml
│               │   └── fragment_tasks_detail.xml
│               ├── values/
│               │   ├── arrays.xml
│               │   ├── attr.xml
│               │   ├── bool.xml
│               │   ├── color.xml
│               │   ├── constants.xml
│               │   ├── dashclock_preference_values.xml
│               │   ├── dimens.xml
│               │   ├── layout_constants.xml
│               │   ├── strings.xml
│               │   ├── styles.xml
│               │   ├── themes.xml
│               │   └── widget_params.xml
│               ├── values-af/
│               │   └── strings.xml
│               ├── values-ar/
│               │   └── strings.xml
│               ├── values-bg/
│               │   └── strings.xml
│               ├── values-ca-rES/
│               │   └── strings.xml
│               ├── values-co/
│               │   └── strings.xml
│               ├── values-cs/
│               │   └── strings.xml
│               ├── values-da/
│               │   └── strings.xml
│               ├── values-de/
│               │   └── strings.xml
│               ├── values-el/
│               │   └── strings.xml
│               ├── values-es/
│               │   └── strings.xml
│               ├── values-et/
│               │   └── strings.xml
│               ├── values-fa/
│               │   └── strings.xml
│               ├── values-fi-rFI/
│               │   └── strings.xml
│               ├── values-fr/
│               │   └── strings.xml
│               ├── values-gl/
│               │   └── strings.xml
│               ├── values-hu/
│               │   └── strings.xml
│               ├── values-in-rID/
│               │   └── strings.xml
│               ├── values-is/
│               │   └── strings.xml
│               ├── values-it/
│               │   └── strings.xml
│               ├── values-iw-rIL/
│               │   └── strings.xml
│               ├── values-ja/
│               │   └── strings.xml
│               ├── values-ko/
│               │   └── strings.xml
│               ├── values-land/
│               │   ├── constants.xml
│               │   └── dimens.xml
│               ├── values-nb-rNO/
│               │   └── strings.xml
│               ├── values-nl/
│               │   └── strings.xml
│               ├── values-pl/
│               │   └── strings.xml
│               ├── values-pt/
│               │   └── strings.xml
│               ├── values-pt-rBR/
│               │   └── strings.xml
│               ├── values-pt-rPT/
│               │   └── strings.xml
│               ├── values-ro/
│               │   └── strings.xml
│               ├── values-ru/
│               │   └── strings.xml
│               ├── values-sk/
│               │   └── strings.xml
│               ├── values-sl/
│               │   └── strings.xml
│               ├── values-sv/
│               │   └── strings.xml
│               ├── values-sw600dp/
│               │   ├── bool.xml
│               │   └── dimens.xml
│               ├── values-sw600dp-land/
│               │   ├── dimens.xml
│               │   └── layout_constants.xml
│               ├── values-sw720dp/
│               │   └── dimens.xml
│               ├── values-sw720dp-land/
│               │   ├── dimens.xml
│               │   └── layout_constants.xml
│               ├── values-ta/
│               │   └── strings.xml
│               ├── values-tr/
│               │   └── strings.xml
│               ├── values-uk/
│               │   └── strings.xml
│               ├── values-v26/
│               │   └── styles.xml
│               ├── values-vec/
│               │   └── strings.xml
│               ├── values-vi/
│               │   └── strings.xml
│               ├── values-zh-rCN/
│               │   └── strings.xml
│               ├── values-zh-rTW/
│               │   └── strings.xml
│               └── xml/
│                   ├── app_pref_backup.xml
│                   ├── app_pref_headers.xml
│                   ├── app_pref_list.xml
│                   ├── app_pref_main.xml
│                   ├── app_pref_notifications.xml
│                   ├── app_pref_sync.xml
│                   ├── dashclock_pref_general.xml
│                   ├── listwidgetinfo.xml
│                   ├── searchable.xml
│                   └── searchabledeleted.xml
├── build.gradle
├── contract/
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── java/
│               └── com/
│                   └── nononsenseapps/
│                       └── notepad/
│                           └── providercontract/
│                               ├── ProviderBase.java
│                               ├── ProviderContract.java
│                               └── UriContract.java
├── customTasks.gradle
├── deploy_playstore.sh
├── documents/
│   ├── CONTRIBUTING.md
│   ├── FAQ.md
│   ├── PRIVACY.md
│   └── TUTORIAL.md
├── external/
│   └── drag-sort-listview/
│       ├── AndroidManifest.xml
│       ├── build.gradle
│       ├── res/
│       │   └── values/
│       │       └── dslv_attrs.xml
│       └── src/
│           └── com/
│               └── mobeta/
│                   └── android/
│                       └── dslv/
│                           ├── DragSortController.java
│                           ├── DragSortCursorAdapter.java
│                           ├── DragSortItemView.java
│                           ├── DragSortItemViewCheckable.java
│                           ├── DragSortListView.java
│                           ├── ResourceDragSortCursorAdapter.java
│                           ├── SimpleDragSortCursorAdapter.java
│                           └── SimpleFloatViewManager.java
├── fastlane/
│   ├── Appfile
│   ├── Fastfile
│   └── metadata/
│       └── android/
│           ├── ar/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── bg/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   └── 71900.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── cs-CZ/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── de-DE/
│           │   ├── changelogs/
│           │   │   └── 57130.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── en-US/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   ├── 72000.txt
│           │   │   ├── 72100.txt
│           │   │   ├── 72200.txt
│           │   │   ├── 72300.txt
│           │   │   └── 72600.txt
│           │   ├── full_description.txt
│           │   ├── images/
│           │   │   ├── featureGraphic.psd
│           │   │   ├── featureGraphic.xcf
│           │   │   └── promoGraphic.psd
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── es-ES/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   └── 71900.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── fi-FI/
│           │   ├── changelogs/
│           │   │   └── 71400.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── fr-FR/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── he/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── id/
│           │   ├── changelogs/
│           │   │   └── 57130.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── is/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── it-IT/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── nb-NO/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   └── 71000.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── pl-PL/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   └── 71700.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── pt-BR/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── pt-PT/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── ro/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── ru-RU/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   ├── 72000.txt
│           │   │   ├── 72100.txt
│           │   │   ├── 72200.txt
│           │   │   ├── 72300.txt
│           │   │   └── 72600.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── ta-IN/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   └── 72000.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── tr-TR/
│           │   ├── changelogs/
│           │   │   ├── 57130.txt
│           │   │   ├── 70000.txt
│           │   │   ├── 71000.txt
│           │   │   ├── 71100.txt
│           │   │   ├── 71200.txt
│           │   │   ├── 71300.txt
│           │   │   ├── 71400.txt
│           │   │   ├── 71500.txt
│           │   │   ├── 71600.txt
│           │   │   ├── 71700.txt
│           │   │   ├── 71800.txt
│           │   │   ├── 71900.txt
│           │   │   ├── 72000.txt
│           │   │   ├── 72100.txt
│           │   │   ├── 72200.txt
│           │   │   ├── 72300.txt
│           │   │   └── 72600.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── vec/
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           └── zh-CN/
│               ├── changelogs/
│               │   └── 70000.txt
│               ├── full_description.txt
│               ├── short_description.txt
│               └── title.txt
├── github_on_emu_started.sh
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── release.sh
└── settings.gradle
Download .txt
SYMBOL INDEX (1574 symbols across 149 files)

FILE: app/dbgenout/com/nononsenseapps/notepad/database/DBItem.java
  class DBItem (line 8) | public abstract class DBItem {
    method DBItem (line 11) | public DBItem() {}
    method DBItem (line 13) | public DBItem(final Cursor cursor) {}
    method getContent (line 15) | public abstract ContentValues getContent();
    method getTableName (line 17) | public abstract String getTableName();
    method getId (line 19) | public abstract long getId();
    method setId (line 21) | public abstract void setId(final long id);
    method getFields (line 23) | public abstract String[] getFields();
    method getUri (line 25) | public Uri getUri() {
    method getBaseUri (line 29) | public Uri getBaseUri() {
    method notifyProvider (line 35) | public void notifyProvider(final Context context) {

FILE: app/dbgenout/com/nononsenseapps/notepad/database/DatabaseHandler.java
  class DatabaseHandler (line 15) | public class DatabaseHandler extends SQLiteOpenHelper {
    method getInstance (line 27) | public synchronized static DatabaseHandler getInstance(Context context) {
    method DatabaseHandler (line 33) | public DatabaseHandler(Context context) {
    method onOpen (line 39) | @Override
    method onCreate (line 55) | @Override
    method onUpgrade (line 68) | @Override
    method putItem (line 76) | public synchronized boolean putItem(final DBItem item) {
    method deleteItem (line 106) | public synchronized int deleteItem(DBItem item) {
    method gettaskviewItemCursor (line 119) | public synchronized Cursor gettaskviewItemCursor(final long id) {
    method gettaskviewItem (line 127) | public synchronized taskviewItem gettaskviewItem(final long id) {
    method getAlltaskviewItemsCursor (line 140) | public synchronized Cursor getAlltaskviewItemsCursor(final String sele...
    method getAlltaskviewItems (line 151) | public synchronized List<taskviewItem> getAlltaskviewItems(final Strin...

FILE: app/dbgenout/com/nononsenseapps/notepad/database/DatabaseTriggers.java
  class DatabaseTriggers (line 5) | public class DatabaseTriggers {
    method create (line 11) | public static void create(final SQLiteDatabase db) {
    method createTemp (line 19) | public static void createTemp(final SQLiteDatabase db) {

FILE: app/dbgenout/com/nononsenseapps/notepad/database/DatabaseViews.java
  class DatabaseViews (line 5) | public class DatabaseViews {
    method create (line 11) | public static void create(final SQLiteDatabase db) {
    method createTemp (line 19) | public static void createTemp(final SQLiteDatabase db) {

FILE: app/dbgenout/com/nononsenseapps/notepad/database/ItemProvider.java
  class ItemProvider (line 9) | public class ItemProvider extends ContentProvider {
    method onCreate (line 20) | @Override
    method delete (line 26) | @Override
    method insert (line 77) | @Override
    method update (line 83) | @Override
    method getType (line 90) | @Override
    method query (line 103) | @Override

FILE: app/dbgenout/com/nononsenseapps/notepad/database/taskviewItem.java
  class taskviewItem (line 11) | public class taskviewItem extends DBItem {
    method URI (line 14) | public static Uri URI() {
    method addMatcherUris (line 41) | public static void addMatcherUris(UriMatcher sURIMatcher) {
    method taskviewItem (line 49) | public taskviewItem() {
    method taskviewItem (line 53) | public taskviewItem(final Cursor cursor) {
    method getContent (line 64) | public ContentValues getContent() {
    method getTableName (line 88) | public String getTableName() {
    method getFields (line 92) | public String[] getFields() {
    method getId (line 96) | public long getId() {
    method setId (line 100) | public void setId(final long id) {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/BaseTestClass.java
  class BaseTestClass (line 28) | public class BaseTestClass {
    method getStringResource (line 52) | public String getStringResource(int resourceId) {
    method clearAppData (line 56) | @After
    method giveNotifyPermission (line 69) | private void giveNotifyPermission() {
    method launchAndWait (line 93) | @Before

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/EspressoHelper.java
  class EspressoHelper (line 33) | public class EspressoHelper {
    method waitUi (line 40) | public static void waitUi() {
    method openDrawer (line 48) | public static void openDrawer() {
    method createNoteWithName (line 58) | public static void createNoteWithName(String noteName) {
    method createNotes (line 70) | public static void createNotes(String[] noteNames) {
    method createTaskList (line 81) | public static void createTaskList(String taskListName) {
    method onViewWithIdInDialog (line 109) | public static ViewInteraction onViewWithIdInDialog(@IdRes int viewId) {
    method isInTabletMode (line 116) | public static boolean isInTabletMode() {
    method navigateUp (line 125) | public static void navigateUp() {
    method exitPrefsActivity (line 139) | public static void exitPrefsActivity() {
    method isShowCaseOverlayVisible (line 168) | private static Boolean isShowCaseOverlayVisible() {
    method hideShowCaseViewIfShown (line 185) | public static void hideShowCaseViewIfShown() {
    method rotateScreenAndWait (line 205) | public static void rotateScreenAndWait() {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddBigNumberOfNotesScrollDownAndDeleteOne.java
  class TestAddBigNumberOfNotesScrollDownAndDeleteOne (line 30) | @LargeTest
    method doesNotHaveViewWithText (line 46) | private static ViewAssertion doesNotHaveViewWithText(final String text) {
    method testAddBigNumberOfNotesScrollDownAndDeleteOne (line 63) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteShouldShowNameInNotesScreen.java
  class TestAddNewNoteShouldShowNameInNotesScreen (line 14) | @LargeTest
    method initStrings (line 19) | @Before
    method testAddNewNoteShouldShowNameInNotesScreen (line 24) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithDueDateCheckDateIsVisible.java
  class TestAddNewNoteWithDueDateCheckDateIsVisible (line 18) | @LargeTest
    method initStrings (line 23) | @Before
    method testAddNewNoteWithDueDateCheckDateIsVisible (line 29) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithReminderDateAndTime.java
  class TestAddNewNoteWithReminderDateAndTime (line 17) | @LargeTest
    method initStrings (line 22) | @Before
    method testAddNewNoteWithReminderDateAndTime (line 27) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNoteToTaskList.java
  class TestAddNoteToTaskList (line 17) | @LargeTest
    method initStrings (line 22) | @Before
    method testAddNoteToTaskList (line 28) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNotesAndRotateScreen.java
  class TestAddNotesAndRotateScreen (line 18) | @LargeTest
    method initStrings (line 23) | @Before
    method testAddNotesAndRotateScreen (line 31) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListAndRotateScreen.java
  class TestAddTaskListAndRotateScreen (line 18) | @LargeTest
    method initStrings (line 23) | @Before
    method testAddTaskListAndRotateScreen (line 28) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListCheckItIsAddedToDrawer.java
  class TestAddTaskListCheckItIsAddedToDrawer (line 15) | @LargeTest
    method initStrings (line 20) | @Before
    method testAddTaskListCheckItIsAddedToDrawer (line 25) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListsScrollNavigationDrawer.java
  class TestAddTaskListsScrollNavigationDrawer (line 23) | @LargeTest
    method testAddTaskListsScrollNavigationDrawer (line 30) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCompletedTasksAreCleared.java
  class TestCompletedTasksAreCleared (line 26) | @LargeTest
    method initStrings (line 31) | @Before
    method testCompletedTasksAreCleared (line 39) | @Test
    method clickCheckBoxAt (line 67) | private void clickCheckBoxAt(int position) {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateNoteAndDeleteIt.java
  class TestCreateNoteAndDeleteIt (line 19) | @LargeTest
    method initStrings (line 24) | @Before
    method testCreateNoteAndDeleteIt (line 29) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateTaskListAndDeleteIt.java
  class TestCreateTaskListAndDeleteIt (line 21) | @LargeTest
    method initStrings (line 26) | @Before
    method testCreateTaskListAndDeleteIt (line 31) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestPasswords.java
  class TestPasswords (line 20) | public class TestPasswords extends BaseTestClass {
    method testAddNoteLockWithPassword (line 31) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestSaveLoadJsonBackup.java
  class TestSaveLoadJsonBackup (line 32) | @LargeTest
    method createResponseIntent (line 42) | private static Instrumentation.ActivityResult createResponseIntent() {
    method testSaveLoadBackup (line 61) | @Test
    method clickCheckBoxAt (line 135) | private void clickCheckBoxAt(int position) {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBFreshTest.java
  class DBFreshTest (line 17) | public class DBFreshTest extends TestCase {
    method setUp (line 23) | @Override
    method tearDown (line 29) | @Override
    method testFreshInstall (line 34) | @SmallTest

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderMovementTest.java
  class DBProviderMovementTest (line 24) | public class DBProviderMovementTest extends TestCase {
    method setUp (line 29) | @Override
    method tearDown (line 38) | @Override
    method assertUriReturnsResult (line 47) | private void assertUriReturnsResult(final Uri uri, final String[] fiel...
    method assertUriReturnsResult (line 51) | private void assertUriReturnsResult(final Uri uri, final String[] fields,
    method assertCursorGood (line 60) | private Cursor assertCursorGood(final Cursor c) {
    method assertTasksCountIs (line 67) | private void assertTasksCountIs(final long listId, final int count) {
    method assertTaskLeftRightAreSequential (line 71) | private void assertTaskLeftRightAreSequential(final long listId) {
    method insertList (line 121) | private TaskList insertList() {
    method deleteList (line 132) | private void deleteList(final TaskList tl) {
    method insertTasks (line 136) | private ArrayList<Task> insertTasks(final long listId, final int numbe...
    method getTasks (line 156) | private ArrayList<Task> getTasks(final long listId) {
    method getTask (line 170) | private Task getTask(final long id) {
    method getDeletedTask (line 183) | private ArrayList<Task> getDeletedTask(final String title) {
    method deleteTasks (line 197) | private void deleteTasks(final ArrayList<Task> tasks) {
    method deleteTask (line 203) | private void deleteTask(Task t) {
    method moveTasksToList (line 208) | private void moveTasksToList(final TaskList tl, final Task... ts) {
    method moveAndAssert (line 228) | private ArrayList<Task> moveAndAssert(final TaskList tl, final int fro...
    method testDeleteList (line 295) | public void testDeleteList() {
    method testInsertAndRemoveTasks (line 318) | public void testInsertAndRemoveTasks() {
    method testInsertTaskInWrongList (line 327) | public void testInsertTaskInWrongList() {
    method testInvalidPos (line 350) | public void testInvalidPos() {
    method testMoveTask (line 383) | public void testMoveTask() {
    method testMoveTaskToList (line 422) | public void testMoveTaskToList() {
    method testTaskContent (line 629) | public void testTaskContent() {
    method testDeleteTrigger (line 639) | public void testDeleteTrigger() {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderTest.java
  class DBProviderTest (line 25) | public class DBProviderTest extends TestCase {
    method setUp (line 30) | @Override
    method assertUriReturnsResult (line 41) | private void assertUriReturnsResult(final Uri uri, final String[] fiel...
    method assertUriReturnsResult (line 45) | private void assertUriReturnsResult(final Uri uri, final String[] fiel...
    method getNewList (line 66) | private TaskList getNewList() {
    method insertSomeTasks (line 73) | private ArrayList<Task> insertSomeTasks(final TaskList list, final int...
    method testTaskListURIs (line 87) | @MediumTest
    method testTaskURIs (line 95) | @MediumTest

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DBUpgradeTest.java
  class DBUpgradeTest (line 21) | public class DBUpgradeTest extends TestCase {
    method setUp (line 33) | @Override
    method tearDown (line 39) | @Override
    method createTables (line 44) | private void createTables(final SQLiteDatabase legacyDB) {
    method initializeDB (line 123) | private void initializeDB(final SQLiteDatabase legacyDB) {
    method testExistingUpgrade (line 249) | @MediumTest

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DaoTaskTest.java
  class DaoTaskTest (line 11) | public class DaoTaskTest extends TestCase {
    method copyValsFromTo (line 13) | private void copyValsFromTo(final Task task1, final Task task2) {
    method testTask (line 20) | @MediumTest

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DashClockSettingsTest.java
  class DashClockSettingsTest (line 31) | public class DashClockSettingsTest {
    method testLoadOK (line 39) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/DateTimeTest.java
  class DateTimeTest (line 12) | public class DateTimeTest extends TestCase {
    method setUp (line 14) | @Override
    method tearDown (line 19) | @Override
    method test_setAsCompleted (line 24) | @SmallTest

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskDetailTest.java
  class FragmentTaskDetailTest (line 15) | public class FragmentTaskDetailTest extends BaseTestClass {
    method testFragmentLoaded (line 17) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsTest.java
  class FragmentTaskListsTest (line 19) | public class FragmentTaskListsTest {
    method testSanity (line 27) | @Test
    method testFragmentLoaded (line 34) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsViewPagerTest.java
  class FragmentTaskListsViewPagerTest (line 14) | public class FragmentTaskListsViewPagerTest {
    method testSanity (line 22) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/Helper.java
  class Helper (line 19) | public class Helper {
    method getATask (line 21) | private static Task getATask(final Context context) {
    method getATaskList (line 31) | private static TaskList getATaskList(final Context context) {
    method takeScreenshot (line 46) | public static void takeScreenshot(String fileName) {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/OrgSyncTest.java
  class OrgSyncTest (line 39) | public class OrgSyncTest {
    method getTheContext (line 48) | private static Context getTheContext() {
    method setUp (line 55) | @Before
    method tearDown (line 70) | @After
    method getTaskLists (line 86) | public ArrayList<TaskList> getTaskLists() {
    method getTasks (line 100) | public ArrayList<Task> getTasks(final long listid) {
    method getRemoteTaskLists (line 116) | public ArrayList<RemoteTaskList> getRemoteTaskLists() {
    method getRemoteTasks (line 132) | public ArrayList<RemoteTask> getRemoteTasks() {
    method testPass (line 148) | @Test
    method testTester (line 154) | @Test
    method testFreshSimple (line 166) | @Test
    method syncAndAssertNothingChanged (line 220) | public void syncAndAssertNothingChanged(final int taskCount) {
    method testNothingNew (line 260) | @Test
    method testDuplicateName (line 277) | @Test
    method testRenamedList (line 320) | @Test
    method testDeletedList (line 365) | @Test
    method testMoveOne (line 415) | @Test
    method testMoveMany (line 486) | @Test
    method testMoveManyAmongMany (line 568) | @Test
    method testFilenameWithSlash (line 692) | @Test
    method testContentStability (line 717) | @Test
    class TestSynchronizer (line 792) | static class TestSynchronizer extends SDSynchronizer {
      method TestSynchronizer (line 796) | public TestSynchronizer(Context context) {
      method isConfigured (line 800) | @Override
      method getServiceName (line 809) | @Override
      method getAccountName (line 814) | @Override
      method putRemoteFile (line 824) | @Override
      method getPutRemoteCount (line 830) | public int getPutRemoteCount() {
      method setPutRemoteCount (line 834) | public void setPutRemoteCount(final int putRemoteCount) {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/ProviderHelperTest.java
  class ProviderHelperTest (line 26) | public class ProviderHelperTest {
    method testGetRelativePath (line 28) | @Test
    method testFirstPart (line 40) | @Test
    method testRestPart (line 49) | @Test
    method testMatchPath (line 59) | @Test
    method testJoin (line 101) | @Test

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/RFCDateTest.java
  class RFCDateTest (line 11) | public class RFCDateTest extends TestCase {
    method test_asRFC3339ZuluDate (line 46) | public void test_asRFC3339ZuluDate() {
    method testParseRFCDateBackAndForth (line 51) | public void testParseRFCDateBackAndForth() {

FILE: app/src/androidTest/java/com/nononsenseapps/notepad/test/StorageTest.java
  class StorageTest (line 14) | public class StorageTest extends TestCase {
    method testIfExternalStorageIsAvailable (line 16) | public void testIfExternalStorageIsAvailable() {

FILE: app/src/main/java/com/google/android/apps/dashclock/ui/DragGripView.java
  class DragGripView (line 31) | public class DragGripView extends View {
    method DragGripView (line 49) | public DragGripView(Context context) {
    method DragGripView (line 53) | public DragGripView(Context context, AttributeSet attrs) {
    method DragGripView (line 57) | public DragGripView(Context context, AttributeSet attrs, int defStyle) {
    method onMeasure (line 74) | @Override
    method onDraw (line 86) | @Override
    method onSizeChanged (line 125) | @Override

FILE: app/src/main/java/com/nononsenseapps/helpers/ActivityHelper.java
  class ActivityHelper (line 37) | public final class ActivityHelper {
    method ActivityHelper (line 43) | private ActivityHelper() {}
    method getUserLocale (line 48) | public static Locale getUserLocale(Context context) {
    method setSelectedLanguage (line 69) | public static void setSelectedLanguage(@NonNull AppCompatActivity cont...

FILE: app/src/main/java/com/nononsenseapps/helpers/DocumentFileHelper.java
  class DocumentFileHelper (line 28) | public final class DocumentFileHelper {
    method isWritableFolder (line 35) | public static boolean isWritableFolder(DocumentFile docDir) {
    method doWithFileDescriptorFor (line 45) | private static boolean doWithFileDescriptorFor(@NonNull DocumentFile t...
    method write (line 73) | public static boolean write(String content, DocumentFile destination, ...
    method createBackupJsonFile (line 94) | public static DocumentFile createBackupJsonFile(Context context) {
    method getSelectedBackupJsonFile (line 117) | @Nullable

FILE: app/src/main/java/com/nononsenseapps/helpers/FileHelper.java
  class FileHelper (line 19) | public final class FileHelper {
    method writeStringToFile (line 27) | @Deprecated
    method getUserSelectedOrgDir (line 57) | public static String getUserSelectedOrgDir(@NonNull Context ctx) {
    method tryDeleteFile (line 79) | public static boolean tryDeleteFile(@NonNull File toDelete, @NonNull C...

FILE: app/src/main/java/com/nononsenseapps/helpers/FilePickerHelper.java
  class FilePickerHelper (line 23) | public final class FilePickerHelper {
    method showFolderPickerActivity (line 37) | public static void showFolderPickerActivity(PreferenceFragmentCompat p...
    method onUriPicked (line 65) | public static void onUriPicked(Intent fromActivityResult, Context cont...

FILE: app/src/main/java/com/nononsenseapps/helpers/ListHelper.java
  class ListHelper (line 34) | public final class ListHelper {
    method getAViewList (line 41) | public static long getAViewList(final Context context, final long temp...
    method getARealList (line 70) | public static long getARealList(final Context context, final long temp...
    method getAShowList (line 129) | public static long getAShowList(final Context context, final long temp...

FILE: app/src/main/java/com/nononsenseapps/helpers/NnnLogger.java
  class NnnLogger (line 27) | public final class NnnLogger {
    method exception (line 32) | public static void exception(@NonNull Exception e) {
    method error (line 47) | public static <T> void error(@NonNull Class<T> caller, @NonNull String...
    method warning (line 63) | public static <T> void warning(@NonNull Class<T> caller, @NonNull Obje...
    method debug (line 79) | public static <T> void debug(@NonNull Class<T> caller, @NonNull Object...

FILE: app/src/main/java/com/nononsenseapps/helpers/NotificationHelper.java
  class NotificationHelper (line 58) | public final class NotificationHelper extends BroadcastReceiver {
    method onReceive (line 78) | @Override
    method getSnoozedReminderNewTimeMillis (line 148) | public static long getSnoozedReminderNewTimeMillis() {
    method createNotificationChannel (line 167) | @TargetApi(Build.VERSION_CODES.O)
    method clearNotification (line 186) | public static void clearNotification(@NonNull final Context context,
    method notifyPast (line 203) | private static void notifyPast(Context context) {
    method getNotificationBuilder (line 280) | private static NotificationCompat.Builder getNotificationBuilder(final...
    method makeUnique (line 308) | private static void makeUnique(final Context context,
    method getLatestOccurence (line 327) | private static List<com.nononsenseapps.notepad.database.Notification> ...
    method getDuplicates (line 343) | private static List<com.nononsenseapps.notepad.database.Notification> ...
    method notifyBigText (line 362) | private static void notifyBigText(final Context context,
    method areNotificationsVisible (line 441) | public static boolean areNotificationsVisible(@NonNull NotificationMan...
    method getLatestTime (line 453) | private static long getLatestTime(
    method scheduleNext (line 470) | private static void scheduleNext(Context context) {
    method useExactReminders (line 526) | private static boolean useExactReminders(Context context, AlarmManager...
    method getTimeForAlarm (line 547) | private static long getTimeForAlarm(com.nononsenseapps.notepad.databas...
    method schedule (line 558) | public static void schedule(final Context context) {
    method updateNotification (line 567) | private static void updateNotification(final Context context,
    method cancelNotification (line 591) | public static void cancelNotification(final Context context,
    method cancelNotification (line 601) | public static void cancelNotification(final Context context, final Uri...
    method cancelNotification (line 609) | public static void cancelNotification(final Context context, final int...
    method getRelatedLists (line 619) | private static Collection<Long> getRelatedLists(
    method getSubList (line 633) | private static List<com.nononsenseapps.notepad.database.Notification> ...

FILE: app/src/main/java/com/nononsenseapps/helpers/PermissionsHelper.java
  class PermissionsHelper (line 29) | public final class PermissionsHelper {
    method hasPermissions (line 42) | public static boolean hasPermissions(@NonNull Context context, String....
    method permissionsGranted (line 58) | public static boolean permissionsGranted(@NonNull String[] permissions,
    method allGranted (line 63) | private static boolean allGranted(int[] items) {

FILE: app/src/main/java/com/nononsenseapps/helpers/PreferencesHelper.java
  class PreferencesHelper (line 34) | public final class PreferencesHelper {
    method Prefs (line 36) | private static SharedPreferences Prefs(@NonNull Context context) {
    method shouldUseExactAlarms (line 40) | public static boolean shouldUseExactAlarms(@NonNull Context context) {
    method isSdSyncEnabled (line 45) | public static boolean isSdSyncEnabled(@NonNull Context context) {
    method disableSdCardSync (line 52) | public static void disableSdCardSync(@NonNull Context context) {
    method isSincEnabledAtAll (line 61) | public static boolean isSincEnabledAtAll(@NonNull Context context) {
    method getStr (line 66) | private static String getStr(@NonNull Context c, int id) {
    method setSortingDue (line 70) | public static void setSortingDue(@NonNull Context context) {
    method setSortingManual (line 77) | public static void setSortingManual(@NonNull Context context) {
    method setSortingAlphabetic (line 84) | public static void setSortingAlphabetic(Context context) {
    method put (line 92) | private static void put(@NonNull Context context, @NonNull String key,...
    method put (line 96) | public static void put(@NonNull Context context, @NonNull String key, ...
    method isPasswordSet (line 103) | public static boolean isPasswordSet(@NonNull Context context) {
    method areAnimationsEnabled (line 111) | public static boolean areAnimationsEnabled(@NonNull Context context) {

FILE: app/src/main/java/com/nononsenseapps/helpers/RFC3339Date.java
  class RFC3339Date (line 28) | public final class RFC3339Date {
    method parseRFC3339Date (line 32) | public static java.util.Date parseRFC3339Date(String datestring) {
    method combineDateAndTime (line 91) | public static Long combineDateAndTime(final String datestring, final L...
    method asRFC3339 (line 125) | public static String asRFC3339(final Long time) {
    method asRFC3339ZuluDate (line 135) | public static String asRFC3339ZuluDate(final Long time) {
    method asRFC3339 (line 152) | private static String asRFC3339(final java.util.Date date) {

FILE: app/src/main/java/com/nononsenseapps/helpers/SyncStatusMonitor.java
  class SyncStatusMonitor (line 33) | public final class SyncStatusMonitor extends BroadcastReceiver {
    method startMonitoring (line 41) | public void startMonitoring(AppCompatActivity activity, OnSyncStartSto...
    method stopMonitoring (line 67) | public void stopMonitoring() {
    method onReceive (line 80) | @Override
    method tellUser (line 113) | private void tellUser(Context context, int result) {
    type OnSyncStartStopListener (line 128) | public interface OnSyncStartStopListener {
      method onSyncStartStop (line 132) | void onSyncStartStop(final boolean ongoing);

FILE: app/src/main/java/com/nononsenseapps/helpers/ThemeHelper.java
  class ThemeHelper (line 18) | public final class ThemeHelper {
    method ThemeHelper (line 21) | private ThemeHelper() {}
    method getThemeAccentColor (line 29) | @ColorInt
    method setTheme (line 40) | public static void setTheme(@NonNull AppCompatActivity activity) {

FILE: app/src/main/java/com/nononsenseapps/helpers/TimeFormatter.java
  class TimeFormatter (line 37) | public final class TimeFormatter {
    method getLocale (line 41) | public static Locale getLocale(final String lang) {
    method getLocalDateString (line 56) | public static String getLocalDateString(final Context context, final S...
    method getLocalDateString (line 65) | public static String getLocalDateString(final Context context,
    method getLocalDateStringLong (line 77) | public static String getLocalDateStringLong(final Context context,
    method getLocalDateOnlyStringLong (line 82) | public static String getLocalDateOnlyStringLong(final Context context,...
    method getLocalTimeOnlyString (line 86) | public static String getLocalTimeOnlyString(final Context context, fin...
    method getLocalDateStringShort (line 105) | public static String getLocalDateStringShort(final Context context,
    method withSuitableTime (line 114) | private static String withSuitableTime(final Context context, final St...
    method withSuitableDateOnly (line 127) | private static String withSuitableDateOnly(final Context context,
    method getLocalFormatter (line 135) | private static SimpleDateFormat getLocalFormatter(final Context context,
    method getLocalCalendar (line 154) | public static GregorianCalendar getLocalCalendar(final Context context) {
    method getLocalFormatterLong (line 164) | public static SimpleDateFormat getLocalFormatterLong(final Context con...
    method getDateFormatter (line 178) | public static SimpleDateFormat getDateFormatter(final Context context) {
    method getLocalFormatterLongDateOnly (line 186) | public static SimpleDateFormat getLocalFormatterLongDateOnly(final Con...
    method getLocalFormatterShort (line 203) | public static SimpleDateFormat getLocalFormatterShort(final Context co...
    method getLocalFormatterShortDateOnly (line 218) | public static SimpleDateFormat getLocalFormatterShortDateOnly(final Co...
    method getLocalFormatterMicro (line 231) | public static SimpleDateFormat getLocalFormatterMicro(final Context co...
    method getLocalFormatterWeekday (line 243) | public static SimpleDateFormat getLocalFormatterWeekday(final Context ...
    method getLocalFormatterWeekdayShort (line 254) | public static SimpleDateFormat getLocalFormatterWeekdayShort(final Con...
    method getHowManyDaysInTheNextMonth (line 265) | public static int getHowManyDaysInTheNextMonth() {
    method getHowManyDaysUntilFirstOfNextMonth (line 275) | public static int getHowManyDaysUntilFirstOfNextMonth() {
    method getHowManyDaysInNextYear (line 285) | public static int getHowManyDaysInNextYear() {
    method getHowManyDaysUntilFirstOfNextYear (line 294) | public static int getHowManyDaysUntilFirstOfNextYear() {

FILE: app/src/main/java/com/nononsenseapps/helpers/UpdateNotifier.java
  class UpdateNotifier (line 40) | public final class UpdateNotifier {
    method notifyChangeNote (line 45) | public static void notifyChangeNote(Context context) {
    method notifyChangeNote (line 55) | public static void notifyChangeNote(Context context, Uri uri) {
    method notifyChangeList (line 63) | public static void notifyChangeList(Context context) {
    method notifyChangeList (line 73) | public static void notifyChangeList(Context context, Uri uri) {
    method notifyChange (line 84) | private static void notifyChange(Context context, Uri uri) {
    method updateWidgets (line 101) | public static void updateWidgets(Context context) {

FILE: app/src/main/java/com/nononsenseapps/notepad/NnnApp.java
  class NnnApp (line 16) | public class NnnApp extends Application {
    method onCreate (line 23) | @Override
    method enableStrictModeAnalysis (line 36) | @RequiresApi(api = Build.VERSION_CODES.P)

FILE: app/src/main/java/com/nononsenseapps/notepad/NotePadBroadcastReceiver.java
  class NotePadBroadcastReceiver (line 33) | public class NotePadBroadcastReceiver extends BroadcastReceiver {
    method onReceive (line 41) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/activities/ActivitySearch.java
  class ActivitySearch (line 37) | public class ActivitySearch extends AppCompatActivity {
    method onCreate (line 46) | @Override
    method getFragment (line 65) | protected Fragment getFragment() {
    method loadContent (line 72) | void loadContent() {
    method onNewIntent (line 79) | @Override
    method handleIntent (line 86) | void handleIntent(Intent intent) {
    method onOptionsItemSelected (line 113) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/activities/ActivitySearchDeleted.java
  class ActivitySearchDeleted (line 25) | public class ActivitySearchDeleted extends ActivitySearch {
    method getFragment (line 27) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/activities/ActivityTaskHistory.java
  class ActivityTaskHistory (line 40) | public class ActivityTaskHistory extends AppCompatActivity {
    method onCreate (line 55) | @Override
    method loadActionBarLayout (line 94) | void loadActionBarLayout() {
    method onStart (line 128) | @Override
    method onSeekBarChanged (line 161) | void onSeekBarChanged(int progress) {
    method setSeekBarProperties (line 177) | void setSeekBarProperties() {
    method onCreateOptionsMenu (line 187) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/activities/main/ActivityMain.java
  class ActivityMain (line 95) | @EActivity(R.layout.activity_main)
    method restoreSavedInstanceState_ (line 170) | private void restoreSavedInstanceState_(Bundle savedInstanceState) {
    method onPostCreate (line 182) | @Override
    method onCreateOptionsMenu (line 189) | @Override
    method onPrepareOptionsMenu (line 201) | @Override
    method onOptionsItemSelected (line 212) | @Override
    method finish (line 278) | @Override
    method openList (line 291) | void openList(final long id) {
    method handleSyncRequest (line 318) | private void handleSyncRequest() {
    method onConfigurationChanged (line 359) | @Override
    method onCreate (line 365) | @Override
    method onDestroy (line 441) | @Override
    method newOnBackPressed (line 453) | void newOnBackPressed() {
    method onPause (line 482) | @Override
    method onNewIntent (line 495) | @Override
    method onResume (line 504) | @Override
    method restartAndRefresh (line 524) | private void restartAndRefresh() {
    method loadFragments (line 534) | @UiThread(propagation = Propagation.REUSE)
    method setHomeAsDrawer (line 651) | void setHomeAsDrawer(final boolean value) {
    method loadContent (line 658) | @AfterViews
    method loadLeftDrawer (line 667) | protected void loadLeftDrawer() {
    method onFragmentInteraction (line 865) | @Override
    method addTaskInList (line 892) | @Override
    method closeFragment (line 922) | @Override
    method simulateBack (line 941) | private void simulateBack() {
    method childItemsVisible (line 951) | @Override
    method onSharedPreferenceChanged (line 956) | @Override
    method addSwipeRefreshLayoutToList (line 977) | public void addSwipeRefreshLayoutToList(SwipeRefreshLayout newSwpRefLa...
    method setRefreshOfAllSwipeLayoutsTo (line 1005) | private void setRefreshOfAllSwipeLayoutsTo(boolean newState) {
    method onSyncStartStop (line 1011) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/activities/main/ActivityMainHelper.java
  class ActivityMainHelper (line 17) | class ActivityMainHelper {
    method getListId (line 24) | static long getListId(final Intent intent) {
    method getNoteShareText (line 58) | static String getNoteShareText(final Intent intent) {
    method getNoteId (line 85) | static long getNoteId(@NonNull final Intent intent) {
    method isNoteIntent (line 109) | static boolean isNoteIntent(final Intent intent) {
    method getListIdToShow (line 134) | static long getListIdToShow(final Intent intent, Context context) {

FILE: app/src/main/java/com/nononsenseapps/notepad/activities/main/DrawerCursorLoader.java
  class DrawerCursorLoader (line 23) | class DrawerCursorLoader implements LoaderManager.LoaderCallbacks<Cursor> {
    method DrawerCursorLoader (line 41) | public DrawerCursorLoader(ActivityMain drawerHost, ArrayList<ArrayList...
    method onCreateLoader (line 48) | @NonNull
    method onLoadFinished (line 72) | @Override
    method updateExtra (line 94) | private void updateExtra(final int pos, final int count) {
    method onLoaderReset (line 103) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/android/provider/DummyProvider.java
  class DummyProvider (line 37) | public class DummyProvider extends ContentProvider {
    method DummyProvider (line 48) | public DummyProvider() {}
    method onCreate (line 50) | @Override
    method initDummyData (line 60) | private List<DummyItem> initDummyData() {
    method getType (line 84) | @Override
    method delete (line 95) | @Override
    method insert (line 102) | @Override
    method query (line 131) | @Override
    method setNotificationUri (line 170) | protected void setNotificationUri(Cursor c, Uri uri) {
    method getNestedList (line 183) | private List<DummyItem> getNestedList(String path) {
    method getNestedItem (line 204) | private DummyItem getNestedItem(String path) {
    method getNestedItem (line 215) | private DummyItem getNestedItem(String path, boolean popItem) {
    method update (line 236) | @Override
    method moveDummyItem (line 273) | private void moveDummyItem(String path, String previous, String parent) {
    method notifyOnChange (line 299) | protected void notifyOnChange(@NonNull Uri uri) {
    class DummyItem (line 309) | private static class DummyItem {
      method DummyItem (line 319) | public DummyItem(@NonNull String path, @NonNull String title) {
      method DummyItem (line 324) | public DummyItem(@NonNull String path, @NonNull String title, long b...
      method DummyItem (line 330) | public DummyItem(@NonNull String path, @NonNull ContentValues values) {
      method asRow (line 337) | public Object[] asRow() {
      method getTitle (line 342) | public String getTitle() {
      method setTitle (line 346) | public void setTitle(@NonNull String title) {
      method getTypemask (line 350) | public long getTypemask() {
      method setTypemask (line 354) | public void setTypemask(long typemask) {
      method getDescription (line 358) | public String getDescription() {
      method setDescription (line 362) | public void setDescription(@Nullable String description) {
      method getStatus (line 366) | public String getStatus() {
      method setStatus (line 370) | public void setStatus(@Nullable String status) {
      method getDue (line 374) | public String getDue() {
      method setDue (line 378) | public void setDue(@Nullable String due) {
      method isDeleted (line 382) | public boolean isDeleted() {
      method setDeleted (line 386) | public void setDeleted(boolean deleted) {
      method getPath (line 390) | public String getPath() {
      method setPath (line 394) | public void setPath(String path) {
      method update (line 398) | public void update(ContentValues values) {

FILE: app/src/main/java/com/nononsenseapps/notepad/android/provider/ProviderHelper.java
  class ProviderHelper (line 32) | public final class ProviderHelper {
    method getListUri (line 50) | public static Uri getListUri(@NonNull Uri base, @NonNull String relati...
    method getDetailsUri (line 64) | @NonNull
    method getBase (line 77) | public static Uri getBase(@NonNull Uri uri) {
    method getRelativePath (line 88) | @NonNull
    method getRelativePath (line 93) | @NonNull
    method firstPart (line 110) | @NonNull
    method restPart (line 131) | @NonNull
    method matchUri (line 143) | public static int matchUri(@NonNull Uri uri) {
    method matchPath (line 155) | public static int matchPath(@Nullable String path) {
    method join (line 197) | @NonNull

FILE: app/src/main/java/com/nononsenseapps/notepad/android/provider/ProviderManager.java
  class ProviderManager (line 35) | public final class ProviderManager {
    method ProviderManager (line 43) | public ProviderManager(@NonNull Context context) {
    method getAvailableProviders (line 50) | @NonNull
    method getConfiguredProviders (line 77) | @NonNull
    method providerHasValidMetadata (line 101) | public boolean providerHasValidMetadata(@NonNull Bundle metadata) {
    method providerRequiresConfig (line 118) | public boolean providerRequiresConfig(@NonNull Bundle metadata) {
    class Provider (line 123) | public static final class Provider {
      method Provider (line 136) | public Provider(@NonNull PackageManager pm, @NonNull ProviderInfo pr...
      method getAuthority (line 150) | @NonNull
      method getUriBase (line 155) | @NonNull
      method getUriList (line 160) | @NonNull
      method getUriDetails (line 165) | @NonNull
      method getLabel (line 170) | @NonNull
      method getIcon (line 175) | public int getIcon() {

FILE: app/src/main/java/com/nononsenseapps/notepad/android/provider/TextFileProvider.java
  class TextFileProvider (line 40) | public class TextFileProvider extends ContentProvider {
    method TextFileProvider (line 74) | public TextFileProvider() {
    method delete (line 77) | @Override
    method getType (line 83) | @Override
    method insert (line 90) | @Override
    method onCreate (line 96) | @Override
    method query (line 121) | @Override
    method update (line 158) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/dashclock/DashclockPrefActivity.java
  class DashclockPrefActivity (line 31) | public class DashclockPrefActivity extends AppCompatActivity {
    method onCreate (line 33) | public void onCreate(Bundle savedInstanceState) {
    method onOptionsItemSelected (line 46) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/dashclock/DashclockPrefsFragment.java
  class DashclockPrefsFragment (line 40) | public class DashclockPrefsFragment extends PreferenceFragmentCompat {
    method onCreatePreferences (line 70) | @Override
    method bindPreferenceSummaryToValue (line 95) | private static void bindPreferenceSummaryToValue(Preference preference) {
    method setEntries (line 111) | private static void setEntries(Context context, ListPreference listSpi...

FILE: app/src/main/java/com/nononsenseapps/notepad/dashclock/TasksExtension.java
  class TasksExtension (line 38) | public class TasksExtension extends DashClockExtension {
    method toA (line 53) | private String[] toA(final String... args) {
    method appendTo (line 57) | private String[] appendTo(final String[] array, final String... items) {
    method onInitialize (line 63) | @Override
    method onUpdateData (line 70) | @Override
    method removeOverdue (line 137) | @SuppressWarnings("unchecked")
    method getBody (line 146) | private String getBody(final ArrayList<Task> notes, final boolean show...
    method getNotesFromDB (line 175) | private ArrayList<Task> getNotesFromDB(final long list, final String u...
    method getHeader (line 204) | private String getHeader(final long list) {
    method getUpperQueryLimitWhere (line 225) | private String getUpperQueryLimitWhere(final String upperLimit) {
    method getUpperQueryLimitWhereArgs (line 233) | private String[] getUpperQueryLimitWhereArgs(final String[] whereArgs,...

FILE: app/src/main/java/com/nononsenseapps/notepad/database/DAO.java
  class DAO (line 33) | public abstract class DAO {
    method whereIdIs (line 40) | public static String whereIdIs(final String orgWhere) {
    method whereIdArg (line 51) | public String[] whereIdArg() {
    method whereIdArg (line 55) | public static String[] whereIdArg(final long _id) {
    method whereIdArg (line 62) | public static String[] whereIdArg(final long _id,
    method prefixArray (line 71) | public static String[] prefixArray(final String prefix, final String[]...
    method joinArrays (line 79) | public static String[] joinArrays(final String[]... arrays) {
    method arrayToCommaString (line 95) | public static String arrayToCommaString(final long... array) {
    method arrayToCommaString (line 105) | public static String arrayToCommaString(final String... array) {
    method arrayToCommaString (line 112) | public static String arrayToCommaString(final String prefix,
    method arrayToCommaString (line 131) | protected static String arrayToCommaString(final String pfx,
    method asEmptyCommaStringExcept (line 153) | protected static String asEmptyCommaStringExcept(final String[] asColu...
    method asEmptyCommaStringExcept (line 177) | protected static String asEmptyCommaStringExcept(final String[] asColu...
    method getUri (line 201) | public Uri getUri() {
    method getBaseUri (line 205) | public Uri getBaseUri() {
    method update (line 213) | public synchronized boolean update(final Context context, final SQLite...
    method insert (line 240) | public synchronized Uri insert(final Context context, final SQLiteData...
    method remove (line 269) | public synchronized int remove(final Context context,
    method notifyProviderOnChange (line 281) | public static void notifyProviderOnChange(final Context context,
    method notifyProviderOnChange (line 290) | protected void notifyProviderOnChange(final Context context) {
    method setId (line 294) | public void setId(final Uri uri) {
    method beforeInsert (line 298) | protected void beforeInsert(final Context context, final SQLiteDatabas...
    method afterInsert (line 300) | protected void afterInsert(final Context context, final SQLiteDatabase...
    method DAO (line 302) | protected DAO() {}
    method getContent (line 304) | public abstract ContentValues getContent();
    method getTableName (line 306) | protected abstract String getTableName();
    method getContentType (line 308) | public abstract String getContentType();
    method save (line 314) | public abstract int save(final Context context);
    method delete (line 321) | public int delete(final Context context) {

FILE: app/src/main/java/com/nononsenseapps/notepad/database/DatabaseHandler.java
  class DatabaseHandler (line 38) | public class DatabaseHandler extends SQLiteOpenHelper {
    method getInstance (line 42) | public static DatabaseHandler getInstance(final Context context) {
    method DatabaseHandler (line 58) | private DatabaseHandler(Context context) {
    method DatabaseHandler (line 65) | public DatabaseHandler(Context context, String testPrefix) {
    method onOpen (line 72) | @Override
    method onCreate (line 84) | @Override
    method getLegacyLists (line 125) | public static Cursor getLegacyLists(final SQLiteDatabase legacyDB) {
    method getLegacyNotes (line 139) | public static Cursor getLegacyNotes(final SQLiteDatabase legacyDB) {
    method getLegacyNotifications (line 160) | public static Cursor getLegacyNotifications(final SQLiteDatabase legac...
    method initializedDB (line 166) | private void initializedDB(final SQLiteDatabase db) throws SQLiteExcep...
    method onUpgrade (line 327) | @Override
    method resetDatabase (line 376) | public static void resetDatabase(Context context) {

FILE: app/src/main/java/com/nononsenseapps/notepad/database/LegacyDBHelper.java
  class LegacyDBHelper (line 43) | public class LegacyDBHelper extends SQLiteOpenHelper {
    method LegacyDBHelper (line 48) | public LegacyDBHelper(Context context) {
    method LegacyDBHelper (line 52) | public LegacyDBHelper(Context context, String testPrefix) {
    method onCreate (line 57) | @Override
    method onUpgrade (line 62) | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersio...
    class NotePad (line 209) | public static final class NotePad {
      method NotePad (line 213) | private NotePad() {
      class Notes (line 219) | public static final class Notes implements BaseColumns {
        method Notes (line 222) | private Notes() {
      class Lists (line 414) | public static final class Lists implements BaseColumns {
        method Lists (line 417) | private Lists() {
      class GTasks (line 552) | public static final class GTasks implements BaseColumns {
        method GTasks (line 555) | private GTasks() {
      class GTaskLists (line 671) | public static final class GTaskLists implements BaseColumns {
        method GTaskLists (line 674) | private GTaskLists() {
      class Notifications (line 790) | public static final class Notifications implements BaseColumns {
        method Notifications (line 793) | private Notifications() {
    method convertLegacyColumns (line 895) | public static String[] convertLegacyColumns(final String[] legacyCols) {
    method convertLegacyTaskValues (line 929) | public static Object[] convertLegacyTaskValues(final Cursor cursor) {

FILE: app/src/main/java/com/nononsenseapps/notepad/database/MyContentProvider.java
  class MyContentProvider (line 40) | public class MyContentProvider extends ContentProvider {
    method MyContentProvider (line 60) | public MyContentProvider() {
    method getType (line 63) | @Override
    method onCreate (line 107) | @Override
    method insert (line 112) | @Override
    method update (line 151) | @Override
    method safeDeleteItem (line 272) | synchronized private int safeDeleteItem(final SQLiteDatabase db,
    method delete (line 293) | @Override
    method query (line 362) | @Override
    method sanitize (line 656) | private String[] sanitize(final String... args) {

FILE: app/src/main/java/com/nononsenseapps/notepad/database/Notification.java
  class Notification (line 49) | public class Notification extends DAO {
    method addMatcherUris (line 88) | public static void addMatcherUris(UriMatcher sURIMatcher) {
    method getUri (line 96) | public static Uri getUri(final long id) {
    class Columns (line 100) | public static class Columns implements BaseColumns {
      method Columns (line 102) | private Columns() {}
    class ColumnsWithTask (line 117) | public static class ColumnsWithTask extends Columns {
      method ColumnsWithTask (line 119) | private ColumnsWithTask() {}
    method Notification (line 219) | public Notification(final long taskID) {
    method Notification (line 223) | public Notification(final Cursor c) {
    method Notification (line 247) | public Notification(final Uri uri, final ContentValues values) {
    method Notification (line 251) | public Notification(final long id, final ContentValues values) {
    method fromUri (line 261) | @Nullable
    method Notification (line 276) | public Notification(final JSONObject json) throws JSONException {
    method Notification (line 295) | public Notification(final ContentValues values) {
    method getContent (line 306) | @Override
    method getTableName (line 322) | @Override
    method getContentType (line 327) | @Override
    method getLocalDateTimeText (line 335) | public CharSequence getLocalDateTimeText(final Context context) {
    method getLocalTimeText (line 342) | public CharSequence getLocalTimeText(final Context context) {
    method getLocalDateText (line 349) | public CharSequence getLocalDateText(final Context context) {
    method save (line 357) | @Override
    method insert (line 380) | private int insert(final Context context) {
    method save (line 393) | public void save(final Context context, final boolean schedule) {
    method delete (line 409) | @Override
    method saveInBackground (line 416) | public void saveInBackground(final Context context, final boolean sche...
    method removeWithTaskIds (line 424) | public static void removeWithTaskIds(final Context context, final Long...
    method removeWithTaskIdsSynced (line 438) | public static void removeWithTaskIdsSynced(final Context context, fina...
    method deleteOrReschedule (line 467) | public static void deleteOrReschedule(final Context context, final Uri...
    method getNotificationsOfTask (line 482) | public static List<Notification> getNotificationsOfTask(final Context ...
    method getNotificationsWithTime (line 492) | public static List<Notification> getNotificationsWithTime(final Contex...
    method getNotificationsWithTasks (line 501) | public static List<Notification> getNotificationsWithTasks(final Conte...
    method setTime (line 523) | public static void setTime(final Context context, final Uri uri, final...
    method setTimeForListAndBefore (line 535) | public static void setTimeForListAndBefore(final Context context, fina...
    method repeatsOn (line 569) | public boolean repeatsOn(final int calendarDay) {
    method deleteOrReschedule (line 588) | public void deleteOrReschedule(final Context context) {
    method getRepeatAsText (line 640) | public String getRepeatAsText(final Context context) {
    method isRepeating (line 669) | public boolean isRepeating() {
    method belongsToNoteInListOfTasks (line 681) | @Nullable

FILE: app/src/main/java/com/nononsenseapps/notepad/database/RemoteTask.java
  class RemoteTask (line 30) | public class RemoteTask extends DAO {
    method addMatcherUris (line 45) | public static void addMatcherUris(UriMatcher sURIMatcher) {
    method getUri (line 52) | public static Uri getUri(final long id) {
    class Columns (line 56) | public static class Columns implements BaseColumns {
      method Columns (line 58) | private Columns() {
    method isDeleted (line 147) | public boolean isDeleted() {
    method setDeleted (line 151) | public void setDeleted(final boolean deleted) {
    method RemoteTask (line 158) | public RemoteTask() {
    method RemoteTask (line 165) | public RemoteTask(final Long dbid, final Long listdbid,
    method RemoteTask (line 174) | public RemoteTask(final Cursor c) {
    method RemoteTask (line 191) | public RemoteTask(final Uri uri, final ContentValues values) {
    method RemoteTask (line 195) | public RemoteTask(final long id, final ContentValues values) {
    method RemoteTask (line 200) | public RemoteTask(final JSONObject json) throws JSONException {
    method RemoteTask (line 225) | public RemoteTask(final ContentValues values) {
    method getContent (line 240) | @Override
    method getTableName (line 260) | @Override
    method getContentType (line 265) | @Override
    method save (line 270) | @Override
    method getTaskWithRemoteClause (line 291) | public String getTaskWithRemoteClause() {
    method getTaskWithRemoteArgs (line 299) | public String[] getTaskWithRemoteArgs() {
    method getTaskWithoutRemoteClause (line 309) | public static String getTaskWithoutRemoteClause() {
    method getTaskWithoutRemoteArgs (line 317) | public static String[] getTaskWithoutRemoteArgs(final long listdbid,

FILE: app/src/main/java/com/nononsenseapps/notepad/database/RemoteTaskList.java
  class RemoteTaskList (line 30) | public class RemoteTaskList extends DAO {
    method addMatcherUris (line 45) | public static void addMatcherUris(UriMatcher sURIMatcher) {
    method getUri (line 52) | public static Uri getUri(final long id) {
    class Columns (line 56) | public static class Columns implements BaseColumns {
      method Columns (line 58) | private Columns() {
    method RemoteTaskList (line 112) | public RemoteTaskList() {
    method RemoteTaskList (line 119) | public RemoteTaskList(final Long dbid, final String remoteId, final Lo...
    method RemoteTaskList (line 127) | public RemoteTaskList(final Cursor c) {
    method RemoteTaskList (line 143) | public RemoteTaskList(final Uri uri, final ContentValues values) {
    method RemoteTaskList (line 147) | public RemoteTaskList(final long id, final ContentValues values) {
    method RemoteTaskList (line 152) | public RemoteTaskList(final JSONObject json) throws JSONException {
    method RemoteTaskList (line 175) | public RemoteTaskList(final ContentValues values) {
    method isDeleted (line 189) | public boolean isDeleted() {
    method setDeleted (line 193) | public void setDeleted(final boolean deleted) {
    method getContent (line 197) | @Override
    method getTableName (line 216) | @Override
    method getContentType (line 221) | @Override
    method save (line 226) | @Override
    method save (line 243) | public int save(final Context context, final long updateTime) {
    method getTaskListWithRemoteClause (line 253) | public String getTaskListWithRemoteClause() {
    method getTaskListWithRemoteArgs (line 261) | public String[] getTaskListWithRemoteArgs() {
    method getTaskListWithoutRemoteClause (line 271) | public static String getTaskListWithoutRemoteClause() {
    method getTaskListWithoutRemoteArgs (line 278) | public String[] getTaskListWithoutRemoteArgs() {

FILE: app/src/main/java/com/nononsenseapps/notepad/database/Task.java
  class Task (line 50) | public class Task extends DAO {
    method getSECTION_DATE_VIEW_NAME (line 65) | public static String getSECTION_DATE_VIEW_NAME(final String listId) {
    method TODAY_PLUS (line 86) | public static String TODAY_PLUS(final int offset) {
    method getUri (line 111) | public static Uri getUri(final long id) {
    method addMatcherUris (line 135) | public static void addMatcherUris(UriMatcher sURIMatcher) {
    method getMoveItemLeftUri (line 201) | private Uri getMoveItemLeftUri() {
    method getMoveItemRightUri (line 208) | private Uri getMoveItemRightUri() {
    class Columns (line 219) | public static class Columns implements BaseColumns {
      method Columns (line 221) | private Columns() {}
    method CREATE_SECTIONED_DATE_VIEW (line 403) | public static String CREATE_SECTIONED_DATE_VIEW(final String listId) {
    method Task (line 577) | public Task() {}
    method resetForInsertion (line 582) | public void resetForInsertion() {
    method setAsCompletedForLegacy (line 593) | public void setAsCompletedForLegacy() {
    method setText (line 603) | public void setText(@NonNull final String text) {
    method getText (line 621) | public String getText() {
    method Task (line 635) | public Task(final Cursor c) {
    method Task (line 651) | public Task(final long id, final ContentValues values) {
    method Task (line 656) | public Task(final Uri uri, final ContentValues values) {
    method Task (line 660) | public Task(final ContentValues values) {
    method Task (line 682) | public Task(final JSONObject json) throws JSONException {
    method getMoveValues (line 707) | public ContentValues getMoveValues(final long targetPos) {
    method getContent (line 719) | @Override
    method equals (line 743) | @Override
    method save (line 766) | public int save(final Context context, final long updated) {
    method save (line 789) | @Override
    method setCompleted (line 802) | public static void setCompleted(final Context context, final boolean c...
    method setCompletedSynced (line 811) | public static void setCompletedSynced(final Context context, final boo...
    method moveTo (line 830) | public int moveTo(final ContentResolver resolver, final Task targetTas...
    method getTableName (line 845) | @Override
    method countVals (line 855) | static String countVals(final String col, final String ver) {
    method posUniqueConstraint (line 863) | static String posUniqueConstraint(final String ver, final String msg) {
    method getSQLMoveItemLeft (line 919) | public String getSQLMoveItemLeft(final ContentValues values) {
    method getSQLMoveItemRight (line 926) | public String getSQLMoveItemRight(final ContentValues values) {
    method getSQLMoveItem (line 960) | private String getSQLMoveItem(final String edgeCol, final Long edgeVal) {
    method getContentType (line 1067) | @Override
    method getHeaderNameForListSortedByDate (line 1089) | public static String getHeaderNameForListSortedByDate(String input, lo...
    method isCompleted (line 1125) | public boolean isCompleted() {
    method byId (line 1133) | public static Task byId(long id, Context context) {

FILE: app/src/main/java/com/nononsenseapps/notepad/database/TaskList.java
  class TaskList (line 34) | public class TaskList extends DAO {
    method getUri (line 45) | public static Uri getUri(final long id) {
    method addMatcherUris (line 63) | public static void addMatcherUris(UriMatcher sURIMatcher) {
    class Columns (line 82) | public static class Columns implements BaseColumns {
      method Columns (line 84) | private Columns() {}
    method TaskList (line 137) | public TaskList() {}
    method TaskList (line 139) | public TaskList(final Cursor c) {
    method TaskList (line 147) | public TaskList(final Uri uri, final ContentValues values) {
    method TaskList (line 151) | public TaskList(final long id, final ContentValues values) {
    method TaskList (line 156) | public TaskList(final JSONObject json) throws JSONException {
    method TaskList (line 167) | public TaskList(final ContentValues values) {
    method getContent (line 174) | public ContentValues getContent() {
    method getTableName (line 184) | @Override
    method getContentType (line 189) | @Override
    method save (line 194) | @Override
    method save (line 202) | public int save(final Context context, final long updateTime) {

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogConfirmBase.java
  class DialogConfirmBase (line 30) | public abstract class DialogConfirmBase extends DialogFragment {
    type DialogConfirmedListener (line 32) | public interface DialogConfirmedListener {
      method onConfirm (line 33) | void onConfirm();
    method setListener (line 38) | public void setListener(final DialogConfirmedListener l) {
    method DialogConfirmBase (line 42) | public DialogConfirmBase() {}
    method onCreateDialog (line 44) | @NonNull
    method getTitle (line 55) | public abstract int getTitle();
    method getMessage (line 57) | public abstract int getMessage();
    method onOKClick (line 59) | public abstract void onOKClick();

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogConfirmBaseV11.java
  class DialogConfirmBaseV11 (line 30) | public abstract class DialogConfirmBaseV11 extends DialogFragment {
    type DialogConfirmedListener (line 32) | public interface DialogConfirmedListener {
      method onConfirm (line 33) | void onConfirm();
    method setListener (line 38) | public void setListener(final DialogConfirmedListener l) {
    method onCreateDialog (line 42) | @NonNull
    method getTitle (line 53) | public abstract int getTitle();
    method getMessage (line 55) | public abstract CharSequence getMessage();
    method onOKClick (line 57) | public abstract void onOKClick();

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteCompletedTasks.java
  class DialogDeleteCompletedTasks (line 31) | public class DialogDeleteCompletedTasks extends DialogDeleteTask {
    method showDialog (line 33) | public static void showDialog(final FragmentManager fm, final long lis...
    method getMessage (line 43) | @Override
    method onOKClick (line 48) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteList.java
  class DialogDeleteList (line 28) | public class DialogDeleteList extends DialogConfirmBase {
    method showDialog (line 33) | public static void showDialog(final FragmentManager fm, final long lis...
    method getTitle (line 43) | @Override
    method getMessage (line 48) | @Override
    method onOKClick (line 53) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteTask.java
  class DialogDeleteTask (line 28) | public class DialogDeleteTask extends DialogConfirmBase {
    method showDialog (line 33) | public static void showDialog(final FragmentManager fm, final long tas...
    method getTitle (line 43) | @Override
    method getMessage (line 48) | @Override
    method onOKClick (line 53) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogEditList.java
  class DialogEditList (line 44) | public class DialogEditList extends DialogFragment {
    type EditListDialogListener (line 46) | public interface EditListDialogListener {
      method onFinishEditDialog (line 47) | void onFinishEditDialog(long id);
    method onCreateView (line 61) | @Nullable
    method onViewCreated (line 69) | @Override
    method onDestroyView (line 91) | @Override
    method getInstance (line 100) | public static DialogEditList getInstance() {
    method getInstance (line 109) | public static DialogEditList getInstance(final long listid) {
    method DialogEditList (line 117) | protected DialogEditList() {}
    method setListener (line 119) | public void setListener(final EditListDialogListener listener) {
    method setup (line 123) | void setup() {
    method fillViews (line 173) | @UiThread
    method deleteClicked (line 189) | void deleteClicked() {
    method okClicked (line 195) | void okClicked() {
    method getSortValue (line 242) | String getSortValue() {
    method selectSortKey (line 259) | void selectSortKey() {
    method getListTypeValue (line 275) | String getListTypeValue() {
    method selectListTypeKey (line 288) | void selectListTypeKey() {

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogExportBackup.java
  class DialogExportBackup (line 28) | public class DialogExportBackup extends DialogConfirmBaseV11 {
    method showDialog (line 32) | public static void showDialog(final FragmentManager fm,
    method getTitle (line 41) | @Override
    method getMessage (line 46) | @Override
    method onOKClick (line 55) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogMoveToList.java
  class DialogMoveToList (line 50) | public class DialogMoveToList extends DialogFragment {
    method onCreateView (line 63) | @Nullable
    method onViewCreated (line 71) | @Override
    method onDestroyView (line 79) | @Override
    method getInstance (line 85) | public static DialogMoveToList getInstance(final Long... tasks) {
    method getInstance (line 94) | public static DialogMoveToList getInstance(final long... taskIds) {
    method DialogMoveToList (line 102) | public DialogMoveToList() {}
    method setup (line 104) | void setup() {
    method moveItems (line 156) | void moveItems(final long toListId, final long[] taskIds) {
    method okClicked (line 168) | void okClicked() {

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogPassword.java
  class DialogPassword (line 38) | public class DialogPassword extends DialogFragment {
    type PasswordConfirmedListener (line 42) | public interface PasswordConfirmedListener {
      method onPasswordConfirmed (line 43) | void onPasswordConfirmed();
    method setListener (line 46) | public void setListener(final PasswordConfirmedListener listener) {
    method onCreateView (line 55) | @Nullable
    method onViewCreated (line 72) | @Override
    method onDestroyView (line 80) | @Override
    method showField (line 86) | public void showField() {
    method confirm (line 97) | void confirm() {
    method checkPassword (line 113) | private void checkPassword(final String enteredPassword, final String ...
    method setPassword (line 130) | private void setPassword(final String pass1, final String pass2) {

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogPasswordV11.java
  class DialogPasswordV11 (line 45) | public class DialogPasswordV11 extends DialogFragment {
    method setListener (line 52) | public void setListener(final PasswordConfirmedListener listener) {
    method onCreateView (line 61) | @Nullable
    method onViewCreated (line 70) | @Override
    method onDestroyView (line 77) | @Override
    method confirm (line 83) | void confirm() {
    method checkPassword (line 95) | private void checkPassword(String enteredPassword, String currentPassw...

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogRestore.java
  class DialogRestore (line 40) | public class DialogRestore extends DialogFragment {
    type OnListSelectedListener (line 42) | public interface OnListSelectedListener {
      method onListSelected (line 43) | void onListSelected(long listId);
    method onCreateView (line 53) | @Nullable
    method onViewCreated (line 61) | @Override
    method onDestroyView (line 69) | @Override
    method getInstance (line 78) | public static DialogRestore getInstance() {
    method DialogRestore (line 84) | public DialogRestore() {}
    method setListener (line 86) | public void setListener(final OnListSelectedListener listener) {
    method setup (line 91) | void setup() {
    method okClicked (line 128) | void okClicked() {

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/DialogRestoreBackup.java
  class DialogRestoreBackup (line 31) | public class DialogRestoreBackup extends DialogConfirmBaseV11 {
    method showDialog (line 35) | public static void showDialog(final FragmentManager fm,
    method getTitle (line 43) | @Override
    method getMessage (line 48) | @Override
    method onOKClick (line 61) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/FragmentSearch.java
  class FragmentSearch (line 55) | public class FragmentSearch extends Fragment {
    method onCreateView (line 67) | @Nullable
    method onViewCreated (line 75) | @Override
    method onDestroyView (line 81) | @Override
    method getInstance (line 87) | public static FragmentSearch getInstance(final String initialQuery) {
    method onCreate (line 95) | @Override
    method onCreateOptionsMenu (line 107) | @Override
    method setupAdapter (line 146) | void setupAdapter() {
    method doSearch (line 177) | protected void doSearch(final String query) {
    method getSearchUri (line 188) | protected Uri getSearchUri() {
    method getFields (line 195) | protected String[] getFields() {
    method getSortOrder (line 202) | protected String getSortOrder() {
    method getAdapter (line 209) | protected SimpleCursorAdapter getAdapter() {
    method getOnItemClickListener (line 226) | protected OnItemClickListener getOnItemClickListener() {
    method getViewBinder (line 234) | protected ViewBinder getViewBinder() {

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/FragmentSearchDeleted.java
  class FragmentSearchDeleted (line 54) | public class FragmentSearchDeleted extends FragmentSearch {
    method onViewCreated (line 56) | @Override
    method getInstance (line 62) | public static FragmentSearchDeleted getInstance(final String initialQu...
    method FragmentSearchDeleted (line 70) | private FragmentSearchDeleted() {}
    method setSelection (line 72) | void setSelection() {
    method getSearchUri (line 202) | @Override
    method getFields (line 207) | @Override
    method getSortOrder (line 212) | @Override
    method getOnItemClickListener (line 217) | @Override
    method getAdapter (line 222) | @Override
    method getViewBinder (line 235) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/TaskDetailFragment.java
  class TaskDetailFragment (line 107) | @EFragment
    method onCreateLoader (line 115) | @Override
    method onLoadFinished (line 136) | @Override
    method onLoaderReset (line 197) | @Override
    method getInstance (line 282) | public static TaskDetailFragment_ getInstance(final Uri itemUri) {
    method getInstance (line 289) | public static TaskDetailFragment_ getInstance(final long itemId) {
    method getInstance (line 300) | public static TaskDetailFragment_ getInstance(String text, final long ...
    method TaskDetailFragment (line 313) | public TaskDetailFragment() {
    method onCreate (line 319) | @Override
    method onCreateView (line 363) | @Override
    method onActivityCreated (line 374) | @Override
    method showcaseEditor (line 442) | boolean showcaseEditor() {
    method setListeners (line 460) | @AfterViews
    method onDateClick (line 484) | @Click(resName = "dueDateBox")
    method onDateSet (line 510) | private void onDateSet(DatePicker dialog, int year, int monthOfYear, i...
    method setDueText (line 538) | private void setDueText() {
    method onDueRemoveClick (line 549) | @Click(resName = "dueCancelButton")
    method onAddReminder (line 559) | @Click(resName = "notificationAdd")
    method isLocked (line 594) | public boolean isLocked() {
    method fillUIFromTask (line 609) | @UiThread(propagation = Propagation.REUSE)
    method setFieldStatus (line 653) | void setFieldStatus() {
    method hideTaskParts (line 667) | void hideTaskParts(final TaskList list) {
    method onCreateOptionsMenu (line 680) | @Override
    method getShareIntent (line 691) | @Nullable
    method onOptionsItemSelected (line 710) | @Override
    method onPrepareOptionsMenu (line 782) | @Override
    method onActivityResult (line 810) | @Override
    method deleteAndClose (line 821) | private void deleteAndClose() {
    method saveTask (line 844) | private void saveTask() {
    method fixIntent (line 866) | void fixIntent() {
    method isThereContent (line 888) | boolean isThereContent() {
    method onPause (line 897) | @Override
    method onStop (line 911) | @Override
    method onAttach (line 925) | @Override
    method onDetach (line 937) | @Override
    method addNotification (line 959) | @UiThread(propagation = Propagation.REUSE)
    method onResume (line 979) | @Override
    method onTimeTravel (line 990) | public void onTimeTravel(Intent data) {
    method getTimePickerDialog (line 1007) | public TimePickerDialog getTimePickerDialog(Calendar localTime,

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/TaskListFragment.java
  class TaskListFragment (line 85) | @EFragment(R.layout.fragment_task_list)
    method getInstance (line 140) | public static TaskListFragment_ getInstance(final long listId) {
    method TaskListFragment (line 148) | public TaskListFragment() {
    method onCreate (line 152) | @Override
    method onActivityCreated (line 303) | @Override
    method whereOverDue (line 426) | public static String whereOverDue() {
    method andWhereOverdue (line 430) | public static String andWhereOverdue() {
    method whereToday (line 434) | public static String whereToday() {
    method andWhereToday (line 439) | public static String andWhereToday() {
    method whereWeek (line 443) | public static String whereWeek() {
    method andWhereWeek (line 448) | public static String andWhereWeek() {
    method setupPullToRefresh (line 452) | @AfterViews
    method loadList (line 463) | @AfterViews
    method onCreateOptionsMenu (line 716) | @Override
    method onPrepareOptionsMenu (line 721) | @Override
    method onOptionsItemSelected (line 734) | @Override
    method onSaveInstanceState (line 768) | @Override
    method onDestroy (line 773) | @Override
    method onAttach (line 779) | @Override
    method onDetach (line 794) | @Override
    class SimpleSectionsAdapter (line 802) | static class SimpleSectionsAdapter extends SimpleDragSortCursorAdapter {
      method SimpleSectionsAdapter (line 812) | public SimpleSectionsAdapter(Context context, int layout,
      method getViewLayout (line 821) | int getViewLayout(final int position) {
      method remove (line 829) | @Override
      method drop (line 836) | @Override
      method setDropListener (line 844) | public void setDropListener(DropListener dropListener) {
      method setRemoveListener (line 848) | public void setRemoveListener(RemoveListener removeListener) {
      method getViewTypeCount (line 852) | @Override
      method getItemViewType (line 857) | @Override
      method getView (line 875) | @Override
      method setPrefsOnView (line 888) | private void setPrefsOnView(final TitleNoteTextView view) {
    method onSharedPreferenceChanged (line 907) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/fragments/TaskListViewPagerFragment.java
  class TaskListViewPagerFragment (line 69) | @EFragment(R.layout.fragment_tasklist_viewpager)
    method getInstance (line 116) | public static TaskListViewPagerFragment getInstance() {
    method getInstance (line 120) | public static TaskListViewPagerFragment getInstance(final long startLi...
    method TaskListViewPagerFragment (line 128) | public TaskListViewPagerFragment() {
    method getSectionsPagerAdapter (line 132) | public SectionsPagerAdapter getSectionsPagerAdapter() {
    method onResume (line 136) | @Override
    method onCreate (line 142) | @Override
    method onActivityCreated (line 163) | @Override
    method setAdapter (line 204) | @AfterViews
    method onCreateOptionsMenu (line 216) | @Override
    method onPrepareOptionsMenu (line 247) | @Override
    method onOptionsItemSelected (line 263) | @Override
    method onFinishEditDialog (line 278) | @Override
    method openList (line 283) | @Override
    method onSaveInstanceState (line 303) | @Override
    method onDestroy (line 315) | @Override
    class SectionsPagerAdapter (line 325) | public class SectionsPagerAdapter extends FragmentPagerAdapter {
      method SectionsPagerAdapter (line 333) | public SectionsPagerAdapter(final FragmentManager fm,
      method destroy (line 372) | public void destroy() {
      method getItem (line 383) | @NonNull
      method getItemId (line 391) | @Override
      method getCount (line 403) | @Override
      method getPageTitle (line 411) | @Override
      method getItemPosition (line 452) | @Override
      method getItemPosition (line 462) | public int getItemPosition(final long listId) {

FILE: app/src/main/java/com/nononsenseapps/notepad/interfaces/ListOpener.java
  type ListOpener (line 9) | public interface ListOpener {
    method openList (line 10) | void openList(final long id);

FILE: app/src/main/java/com/nononsenseapps/notepad/interfaces/MenuStateController.java
  type MenuStateController (line 23) | public interface MenuStateController {
    method childItemsVisible (line 29) | boolean childItemsVisible();

FILE: app/src/main/java/com/nononsenseapps/notepad/interfaces/OnFragmentInteractionListener.java
  type OnFragmentInteractionListener (line 13) | public interface OnFragmentInteractionListener {
    method onFragmentInteraction (line 15) | void onFragmentInteraction(final Uri uri, final long listId, final Vie...
    method addTaskInList (line 17) | void addTaskInList(final String text, final long listId);
    method closeFragment (line 19) | void closeFragment(final Fragment fragment);

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/AboutPrefs.java
  class AboutPrefs (line 33) | public class AboutPrefs extends Fragment {
    method onCreateView (line 40) | @Override
    method onViewCreated (line 47) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/AppearancePrefs.java
  class AppearancePrefs (line 38) | public class AppearancePrefs extends PreferenceFragmentCompat {
    method onCreatePreferences (line 52) | @Override
    method initializeRestartingPrefWithKey (line 86) | private void initializeRestartingPrefWithKey(String prefKey) {
    method setDateEntries (line 104) | private void setDateEntries(ListPreference prefDate, int array) {
    method setLangEntries (line 121) | private static void setLangEntries(ListPreference prefLang, Context co...

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/BackupPrefs.java
  class BackupPrefs (line 45) | public class BackupPrefs extends PreferenceFragmentCompat {
    method onCreatePreferences (line 59) | @Override
    method onUriDirPrefChange (line 95) | private static void onUriDirPrefChange(Preference directoryUriPreferen...
    method getSelectedBackupDirUri (line 107) | @Nullable
    method onActivityResult (line 115) | @Override
    method runBackupOrRestore (line 132) | private void runBackupOrRestore(boolean isRestoring) {
    method asyncTask_doInBackground (line 163) | private static int asyncTask_doInBackground(boolean isRestoring, JSONB...
    method asyncTask_onPostExecute (line 185) | private static void asyncTask_onPostExecute(@NonNull Context mContext,

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/Constants.java
  class Constants (line 6) | public final class Constants {

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/IndexPrefs.java
  class IndexPrefs (line 14) | public class IndexPrefs extends PreferenceFragmentCompat {
    method onCreatePreferences (line 16) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/ListPrefs.java
  class ListPrefs (line 34) | public class ListPrefs extends PreferenceFragmentCompat {
    method onCreatePreferences (line 36) | @Override
    method setEntries (line 91) | private void setEntries(ListPreference listSpinner) {

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/NotificationPrefs.java
  class NotificationPrefs (line 51) | public class NotificationPrefs extends PreferenceFragmentCompat {
    method onCreatePreferences (line 55) | @Override
    method onPreferenceTreeClick (line 83) | @Override
    method onActivityResult (line 163) | @Override
    method updateRingtonePrefSummary (line 199) | private static void updateRingtonePrefSummary(Preference theRingtonePr...
    method onResume (line 225) | @Override
    method openNotificationSettings (line 252) | private static void openNotificationSettings(Context context) {
    method showHibernationPageIfNeeded (line 279) | static void showHibernationPageIfNeeded(@NonNull PreferenceFragmentCom...

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/PasswordPrefs.java
  class PasswordPrefs (line 38) | public class PasswordPrefs extends Fragment {
    method onCreateView (line 49) | @Nullable
    method onViewCreated (line 59) | @Override
    method applyPassword (line 66) | private void applyPassword() {
    method clearPassword (line 98) | private void clearPassword() {
    method showPasswordDialog (line 116) | private void showPasswordDialog(final String newPassword) {

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/PrefsActivity.java
  class PrefsActivity (line 43) | public class PrefsActivity extends AppCompatActivity implements
    method onCreate (line 48) | @Override
    method onPreferenceStartFragment (line 106) | @Override
    method onDestroy (line 142) | @Override
    method onOptionsItemSelected (line 152) | @Override
    method bindSummaryToValue (line 197) | public static void bindSummaryToValue(Preference preference) {

FILE: app/src/main/java/com/nononsenseapps/notepad/prefs/SyncPrefs.java
  class SyncPrefs (line 41) | public class SyncPrefs extends PreferenceFragmentCompat
    method onCreatePreferences (line 54) | @Override
    method onDestroy (line 82) | @Override
    method onSharedPreferenceChanged (line 99) | public void onSharedPreferenceChanged(SharedPreferences prefs, String ...
    method onActivityResult (line 124) | @Override
    method afterGettingAuthToken (line 144) | private void afterGettingAuthToken(AccountManagerFuture<Bundle> future...

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/SyncAdapter.java
  class SyncAdapter (line 23) | public final class SyncAdapter {
    method SyncAdapter (line 25) | private SyncAdapter() {}

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/files/JSONBackup.java
  class JSONBackup (line 46) | public class JSONBackup {
    method JSONBackup (line 55) | public JSONBackup(final Context context) {
    method getTaskLists (line 60) | private List<TaskList> getTaskLists() {
    method getRemotesOf (line 78) | private List<RemoteTaskList> getRemotesOf(final TaskList list) {
    method getTasksIn (line 97) | private List<Task> getTasksIn(final TaskList list) {
    method getRemotesOf (line 116) | private List<RemoteTask> getRemotesOf(final Task task) {
    method getRemindersFor (line 137) | private List<Notification> getRemindersFor(final Task task) {
    method getJSONBackup (line 158) | private JSONObject getJSONBackup() throws JSONException {
    method addAllContentToJSON (line 176) | private void addAllContentToJSON(final ContentValues content,
    method getJSONRemotesFor (line 183) | private JSONArray getJSONRemotesFor(final TaskList list)
    method getJSONTasksFor (line 196) | private JSONArray getJSONTasksFor(final TaskList list) throws JSONExce...
    method getJSONRemotesFor (line 213) | private JSONArray getJSONRemotesFor(final Task task) throws JSONExcept...
    method getJSONRemindersFor (line 225) | private JSONArray getJSONRemindersFor(final Task task) throws JSONExce...
    method writeBackup (line 241) | public void writeBackup() throws JSONException, IOException, SecurityE...
    method restoreBackup (line 264) | public void restoreBackup() throws SecurityException, JSONException, I...
    method clearDatabase (line 292) | private void clearDatabase() {
    method readBackup (line 303) | private JSONObject readBackup() throws JSONException, IOException, Sec...
    method restoreRemotes (line 323) | private void restoreRemotes(final TaskList tasklist,
    method restoreTasks (line 335) | private void restoreTasks(final TaskList list, final JSONArray tasksar...
    method restoreRemotes (line 355) | private void restoreRemotes(final Task task, final JSONArray jsonArray)
    method restoreReminders (line 367) | private void restoreReminders(final Task task, final JSONArray jsonArray)

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/googleapi/GoogleTask.java
  class GoogleTask (line 24) | public class GoogleTask extends RemoteTask {
    method GoogleTask (line 53) | public GoogleTask(final Task dbTask, final String accountName) {
    method fillFrom (line 62) | public void fillFrom(final Task dbTask) {
    method equals (line 77) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/googleapi/GoogleTaskList.java
  class GoogleTaskList (line 23) | public class GoogleTaskList extends RemoteTaskList {
    method GoogleTaskList (line 28) | public GoogleTaskList(final TaskList dbList, final String accountName) {
    method GoogleTaskList (line 36) | public GoogleTaskList(final Long dbid, final String remoteId, final Lo...
    method equals (line 45) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/BackgroundSyncScheduler.java
  class BackgroundSyncScheduler (line 31) | public class BackgroundSyncScheduler extends BroadcastReceiver {
    method BackgroundSyncScheduler (line 36) | public BackgroundSyncScheduler() {}
    method onReceive (line 38) | @Override
    method scheduleSync (line 55) | public static void scheduleSync(final Context context) {

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/DBSyncBase.java
  class DBSyncBase (line 48) | public abstract class DBSyncBase implements SynchronizerInterface {
    method DBSyncBase (line 53) | public DBSyncBase(final Context context) {
    method getNodesAndDBEntries (line 68) | protected List<Pair<OrgNode, Pair<RemoteTask, Task>>> getNodesAndDBEnt...
    method getNodes (line 109) | private HashMap<String, OrgNode> getNodes(final OrgFile file) {
    method addNodeToMap (line 122) | private void addNodeToMap(final OrgNode node,
    method getValidRemoteTasks (line 137) | private HashMap<Long, RemoteTask> getValidRemoteTasks(final TaskList l...
    method getInvalidRemoteTasks (line 162) | private List<RemoteTask> getInvalidRemoteTasks(final TaskList list) {
    method getTasks (line 182) | private HashMap<Long, Task> getTasks(final TaskList list) {
    method getFilesAndDBEntries (line 201) | protected List<Pair<OrgFile, Pair<RemoteTaskList, TaskList>>> getFiles...
    method getRemoteTaskLists (line 285) | private HashMap<Long, RemoteTaskList> getRemoteTaskLists() {
    method getLists (line 304) | private HashMap<Long, TaskList> getLists() {
    method replaceNotifications (line 321) | protected void replaceNotifications(final Task task, final OrgNode nod...
    method wasRenamed (line 333) | protected boolean wasRenamed(final TaskList list, final OrgFile file) {
    method renameFile (line 345) | protected void renameFile(final TaskList list,
    method deleteRemoteTasksIn (line 359) | private void deleteRemoteTasksIn(final long listdbid) {
    method deleteLocal (line 374) | protected void deleteLocal(final TaskList list, final RemoteTaskList d...
    method deleteLocal (line 395) | protected void deleteLocal(final Task task, final RemoteTask dbEntry) {

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/Monitor.java
  type Monitor (line 23) | public interface Monitor {
    method startMonitor (line 28) | void startMonitor(final OrgSyncService.SyncHandler handler);
    method pauseMonitor (line 33) | void pauseMonitor();
    method terminate (line 38) | void terminate();

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgConverter.java
  class OrgConverter (line 44) | public class OrgConverter {
    method generateId (line 64) | public static String generateId() {
    method toListFromFile (line 81) | public static void toListFromFile(final TaskList list, final OrgFile f...
    method getListTypeFromMeta (line 92) | public static String getListTypeFromMeta(final OrgFile file) {
    method getListSortingFromMeta (line 104) | public static String getListSortingFromMeta(final OrgFile file) {
    method toRemoteFromFile (line 117) | public static void toRemoteFromFile(final RemoteTaskList entry,
    method toTaskFromNode (line 128) | public static void toTaskFromNode(final Task task, final OrgNode node) {
    method toNodeFromTask (line 146) | public static void toNodeFromTask(final Task task, final OrgNode node) {
    method setNotifications (line 155) | private static void setNotifications(final OrgNode node, final List<No...
    method getCompleted (line 171) | private static Long getCompleted(final OrgNode node) {
    method setTodo (line 179) | private static void setTodo(final OrgNode node, final Long completed) {
    method getDeadline (line 190) | public static Long getDeadline(final OrgNode node) {
    method removeTimestamps (line 199) | private static void removeTimestamps(final OrgNode node) {
    method setDeadline (line 203) | public static void setDeadline(final OrgNode node, final Long due) {
    method getOrgBodySansId (line 216) | private static String getOrgBodySansId(final OrgNode node) {
    method toRemoteFromNode (line 226) | public static boolean toRemoteFromNode(final RemoteTask dbEntry,
    method addIdToNode (line 255) | @SuppressLint("DefaultLocale")
    method toNodeFromRemote (line 264) | @SuppressLint("DefaultLocale")
    method getNodeId (line 273) | @SuppressLint("DefaultLocale")
    method toFileFromList (line 287) | public static void toFileFromList(final TaskList list, final OrgFile f...
    method getTitleAsFilename (line 292) | public static String getTitleAsFilename(TaskList list) {
    method setListTypeOnFile (line 296) | public static void setListTypeOnFile(TaskList list, OrgFile file) {
    method setSortingOnFile (line 308) | public static void setSortingOnFile(final TaskList list, final OrgFile...

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgProvider.java
  class OrgProvider (line 26) | public class OrgProvider extends ContentProvider {
    method OrgProvider (line 44) | public OrgProvider() {}
    method deleteItem (line 46) | private int deleteItem(Uri uri) {
    method writeToFile (line 78) | private static void writeToFile(File file, OrgFile orgFile) throws IOE...
    method deleteFile (line 87) | private int deleteFile(Uri uri) {
    method getFragment (line 100) | @Nullable
    method insertItem (line 109) | private Uri insertItem(Uri uri, ContentValues values) {
    method insertFile (line 114) | private Uri insertFile(Uri uri, ContentValues values) {
    method getDir (line 136) | private String getDir() {
    method onCreate (line 140) | @Override
    method query (line 145) | @Override
    method getType (line 170) | @Override
    method insert (line 175) | @Override
    method delete (line 194) | @Override
    method update (line 209) | @Override
    method queryItem (line 219) | private Cursor queryItem(Uri uri, String[] projection, String selection,
    method queryFile (line 224) | private Cursor queryFile(Uri uri, String[] projection, String selection,
    method queryFiles (line 229) | private Cursor queryFiles(Uri uri, String[] projection, String selection,

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgSyncService.java
  class OrgSyncService (line 48) | public class OrgSyncService extends Service {
    method start (line 65) | public static void start(Context context) {
    method pause (line 77) | public static void pause(Context context) {
    method stop (line 82) | public static void stop(Context context) {
    method areAnyEnabled (line 86) | public static boolean areAnyEnabled(Context context) {
    method OrgSyncService (line 93) | public OrgSyncService() {
    method getSynchronizers (line 103) | public ArrayList<SynchronizerInterface> getSynchronizers() {
    method onCreate (line 117) | @Override
    method onStartCommand (line 132) | @Override
    method pause (line 147) | private void pause() {
    method onDestroy (line 154) | @Override
    method onBind (line 162) | @Override
    class SyncHandler (line 168) | public final class SyncHandler extends Handler {
      method SyncHandler (line 173) | public SyncHandler(Looper looper) {
      method onMonitorChange (line 177) | public void onMonitorChange() {
      method handleMessage (line 195) | @Override
    class DBWatcher (line 266) | private final class DBWatcher extends ContentObserver implements Monit...
      method DBWatcher (line 271) | public DBWatcher(SyncHandler handler) {
      method deliverSelfNotifications (line 276) | public boolean deliverSelfNotifications() {
      method onChange (line 280) | @Override
      method onChange (line 285) | @Override
      method startMonitor (line 290) | @Override
      method pauseMonitor (line 299) | @Override
      method terminate (line 304) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/RemoteTaskListFile.java
  class RemoteTaskListFile (line 22) | public class RemoteTaskListFile {
    method getSorting (line 24) | public static String getSorting(final RemoteTaskList remote) {
    method getListType (line 28) | public static String getListType(final RemoteTaskList remote) {
    method setSorting (line 32) | public static void setSorting(final RemoteTaskList remote, final Strin...
    method setListType (line 36) | public static void setListType(final RemoteTaskList remote, final Stri...

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/RemoteTaskNode.java
  class RemoteTaskNode (line 22) | public class RemoteTaskNode {
    method getTitle (line 24) | public static String getTitle(final RemoteTask remote) {
    method getBody (line 28) | public static String getBody(final RemoteTask remote) {
    method getDueTime (line 32) | public static String getDueTime(final RemoteTask remote) {
    method getTodo (line 36) | public static String getTodo(final RemoteTask remote) {
    method setTitle (line 40) | public static void setTitle(final RemoteTask remote, final String titl...
    method setBody (line 44) | public static void setBody(final RemoteTask remote, final String body) {
    method setDueTime (line 48) | public static void setDueTime(final RemoteTask remote, final String s) {
    method setTodo (line 52) | public static void setTodo(final RemoteTask remote, final String s) {

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/SDSynchronizer.java
  class SDSynchronizer (line 48) | public class SDSynchronizer extends Synchronizer implements Synchronizer...
    method SDSynchronizer (line 62) | public SDSynchronizer(Context context) {
    method getAccountName (line 82) | @Override
    method getServiceName (line 90) | @Override
    method isConfigured (line 100) | @Override
    method getNewFile (line 122) | @Override
    method putRemoteFile (line 149) | @Override
    method deleteRemoteFile (line 178) | @Override
    method renameRemoteFile (line 191) | @Override
    method getRemoteFile (line 206) | @Override
    method getRemoteFilenames (line 223) | @SuppressLint("DefaultLocale")
    method postSynchronize (line 242) | @Override
    method getMonitor (line 247) | @Override
    class FileWatcher (line 252) | public static class FileWatcher extends FileObserver implements Monitor {
      method FileWatcher (line 257) | public FileWatcher(String path) {
      method onEvent (line 264) | @Override
      method startMonitor (line 271) | @Override
      method pauseMonitor (line 277) | @Override
      method terminate (line 283) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/Synchronizer.java
  class Synchronizer (line 38) | public abstract class Synchronizer extends DBSyncBase implements Synchro...
    method Synchronizer (line 45) | public Synchronizer(Context context) {
    method fullSync (line 52) | public void fullSync() throws IOException, ParseException {
    method merge (line 166) | private int merge(final TaskList list, final RemoteTaskList dbEntry, f...
    method mergeSorting (line 175) | private int mergeSorting(final TaskList list, final RemoteTaskList dbE...
    method mergeListType (line 197) | private int mergeListType(final TaskList list, final RemoteTaskList db...
    method syncTasks (line 219) | private boolean syncTasks(final Context context, final TaskList list, ...
    method deleteNode (line 319) | private void deleteNode(final OrgNode node) {
    method merge (line 341) | protected int merge(final Task task, final RemoteTask remote, final Or...
    method mergeTodo (line 359) | private int mergeTodo(final Task task, final RemoteTask remote, final ...
    method mergeTimestamps (line 385) | private int mergeTimestamps(final Task task, final RemoteTask remote, ...
    method mergeBodies (line 408) | private int mergeBodies(final Task task, final RemoteTask remote, fina...
    method mergeTitles (line 436) | private int mergeTitles(final Task task, final RemoteTask remote, fina...

FILE: app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/SynchronizerInterface.java
  type SynchronizerInterface (line 30) | public interface SynchronizerInterface {
    method getServiceName (line 36) | String getServiceName();
    method getAccountName (line 41) | String getAccountName();
    method isConfigured (line 48) | boolean isConfigured();
    method getNewFile (line 60) | OrgFile getNewFile(final String desiredName) throws IOException, Illeg...
    method putRemoteFile (line 67) | void putRemoteFile(final OrgFile orgFile) throws IOException;
    method deleteRemoteFile (line 74) | void deleteRemoteFile(final OrgFile orgFile) throws IOException;
    method renameRemoteFile (line 82) | void renameRemoteFile(final String oldName, final OrgFile orgFile) thr...
    method getRemoteFile (line 89) | BufferedReader getRemoteFile(final String filename) throws IOException;
    method getRemoteFilenames (line 94) | HashSet<String> getRemoteFilenames() throws IOException;
    method fullSync (line 99) | void fullSync() throws IOException, ParseException;
    method postSynchronize (line 104) | void postSynchronize();
    method getMonitor (line 109) | Monitor getMonitor();

FILE: app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetConfig.java
  class ListWidgetConfig (line 65) | public class ListWidgetConfig extends AppCompatActivity {
    method onCreate (line 118) | @Override
    method setupPreview (line 145) | void setupPreview() {
    method reloadTasks (line 362) | void reloadTasks() {
    method setupActionBar (line 366) | void setupActionBar() {
    method setupConfig (line 401) | void setupConfig() {
    method getListPositionOf (line 592) | private static int getListPositionOf(final Adapter adapter, final long...
    method getSpinnerPositionOf (line 604) | private static int getSpinnerPositionOf(final Adapter adapter, final S...
    method getArrayPositionOf (line 616) | private static int getArrayPositionOf(final String[] array, final Stri...
    method updateBG (line 628) | void updateBG(final int color) {
    method updateTheme (line 634) | void updateTheme(final int theme, final WidgetPrefs widgetPrefs) {
    method getHomescreenBackgroundColor (line 661) | public static int getHomescreenBackgroundColor(final int opacity) {
    method getHomescreenBackgroundColor (line 675) | public static int getHomescreenBackgroundColor(final int opacity, fina...
    class SimpleWidgetPreviewAdapter (line 682) | static class SimpleWidgetPreviewAdapter extends SimpleCursorAdapter {
      method SimpleWidgetPreviewAdapter (line 689) | public SimpleWidgetPreviewAdapter(Context context, int layout, int h...
      method getViewLayout (line 697) | int getViewLayout(final int position) {
      method getViewTypeCount (line 705) | @Override
      method getItemViewType (line 710) | @Override
      method getView (line 721) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetProvider.java
  class ListWidgetProvider (line 45) | public class ListWidgetProvider extends AppWidgetProvider {
    method ListWidgetProvider (line 57) | public ListWidgetProvider() {}
    method onReceive (line 63) | @Override
    method onEnabled (line 102) | @Override
    method onDeleted (line 114) | @Override
    method onUpdate (line 125) | @Override
    method buildRemoteViews (line 142) | public static RemoteViews buildRemoteViews(final Context context,
    method getThePendingIntentForActivity (line 262) | @SuppressLint("UnspecifiedImmutableFlag")
    method getThePendingIntentForBroadcast (line 280) | @SuppressLint("UnspecifiedImmutableFlag")

FILE: app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetService.java
  class ListWidgetService (line 43) | public class ListWidgetService extends RemoteViewsService {
    method onGetViewFactory (line 45) | @Override
    class ListRemoteViewsFactory (line 53) | static class ListRemoteViewsFactory implements RemoteViewsService.Remo...
      method ListRemoteViewsFactory (line 66) | public ListRemoteViewsFactory(Context context, Intent intent) {
      method onCreate (line 72) | @Override
      method onDestroy (line 75) | @Override
      method getCount (line 81) | @Override
      method getViewAt (line 87) | @Override
      method getLoadingView (line 242) | @Override
      method getViewTypeCount (line 247) | @Override
      method getItemId (line 252) | @Override
      method hasStableIds (line 257) | @Override
      method onDataSetChanged (line 262) | @Override

FILE: app/src/main/java/com/nononsenseapps/notepad/widget/list/WidgetPrefs.java
  class WidgetPrefs (line 26) | public class WidgetPrefs {
    method delete (line 36) | public static void delete(final Context context, final int widgetId) {
    method WidgetPrefs (line 46) | public WidgetPrefs(final Context context, final int widgetId) {
    method keyWrap (line 51) | public String keyWrap(final String originalKey) {
    method keyWrap (line 55) | public static String keyWrap(final String originalKey, final int widge...
    method isPresent (line 59) | public boolean isPresent() {
    method setPresent (line 66) | public void setPresent() {
    method putBoolean (line 70) | public boolean putBoolean(String key, boolean value) {
    method getBoolean (line 81) | public boolean getBoolean(String key, boolean defValue) {
    method putString (line 92) | public void putString(String key, String value) {
    method getString (line 101) | public String getString(String key, String defValue) {
    method getInt (line 112) | public int getInt(String key, int defValue) {
    method putInt (line 123) | public void putInt(String key, int value) {
    method putLong (line 132) | public void putLong(String key, long value) {
    method getLong (line 141) | public long getLong(String key, long defValue) {

FILE: app/src/main/java/com/nononsenseapps/notepad/widget/shortcut/ShortcutConfig.java
  class ShortcutConfig (line 39) | public class ShortcutConfig extends AppCompatActivity {
    method onCreate (line 46) | @Override
    method getBitmapFromDrawable (line 65) | @NonNull
    method onOK (line 77) | void onOK() {
    method setListEntries (line 168) | private void setListEntries(final Spinner listSpinner) {

FILE: app/src/main/java/com/nononsenseapps/ui/DateView.java
  class DateView (line 37) | public class DateView extends AppCompatTextView {
    method DateView (line 45) | public DateView(Context context) {
    method DateView (line 54) | public DateView(Context context, AttributeSet attrs) {
    method DateView (line 65) | public DateView(Context context, AttributeSet attrs, int defStyle) {
    method setTimeText (line 71) | public void setTimeText(final long time) {
    method toDate (line 75) | public static CharSequence toDate(String format, long msecs) {

FILE: app/src/main/java/com/nononsenseapps/ui/DelegateFrame.java
  class DelegateFrame (line 52) | public class DelegateFrame extends RelativeLayout implements OnClickList...
    method DelegateFrame (line 60) | public DelegateFrame(Context context) {
    method DelegateFrame (line 64) | public DelegateFrame(Context context, AttributeSet attrs) {
    method DelegateFrame (line 68) | public DelegateFrame(Context context, AttributeSet attrs, int defStyle) {
    method onClick (line 80) | @Override

FILE: app/src/main/java/com/nononsenseapps/ui/ExtraTypesCursorAdapter.java
  class ExtraTypesCursorAdapter (line 32) | public class ExtraTypesCursorAdapter extends ExtrasCursorAdapter {
    method ExtraTypesCursorAdapter (line 45) | public ExtraTypesCursorAdapter(Context context, int layout, Cursor c,
    method countTypes (line 54) | private int countTypes() {
    method getViewTypeCount (line 64) | @Override
    method getItemViewType (line 69) | @Override
    method getItemLayout (line 78) | @Override
    method setExtraText (line 88) | @Override
    method setExtraData (line 107) | public void setExtraData(ArrayList<ArrayList<Object>> extras) {

FILE: app/src/main/java/com/nononsenseapps/ui/ExtrasCursorAdapter.java
  class ExtrasCursorAdapter (line 36) | public class ExtrasCursorAdapter extends ResourceCursorAdapter {
    method ExtrasCursorAdapter (line 57) | public ExtrasCursorAdapter(Context context, int layout, Cursor c,
    method ExtrasCursorAdapter (line 76) | public ExtrasCursorAdapter(Context context, int layout, Cursor c,
    method bindView (line 90) | @Override
    method setViewHolder (line 108) | private ViewHolder setViewHolder(View view) {
    method swapCursor (line 119) | @Override
    method getView (line 125) | @Override
    method setExtraText (line 149) | protected void setExtraText(final ViewHolder viewHolder, final int pos...
    method getDropDownView (line 155) | @Override
    method getItemLayout (line 176) | protected int getItemLayout(final int position) {
    method getItemId (line 180) | @Override
    method getItem (line 192) | @Override
    method getCount (line 201) | @Override
    method getExtraItem (line 212) | public CharSequence getExtraItem(int realPos) {
    class ViewHolder (line 219) | static class ViewHolder {

FILE: app/src/main/java/com/nononsenseapps/ui/GreyableToggleButton.java
  class GreyableToggleButton (line 34) | public class GreyableToggleButton extends AppCompatToggleButton {
    method GreyableToggleButton (line 43) | public GreyableToggleButton(Context context, AttributeSet attrs) {
    method getTextColorPrimary (line 52) | private static ColorStateList getTextColorPrimary(Context context) {
    method setChecked (line 60) | @Override

FILE: app/src/main/java/com/nononsenseapps/ui/NoteCheckBox.java
  class NoteCheckBox (line 29) | public class NoteCheckBox extends AppCompatCheckBox {
    method NoteCheckBox (line 32) | public NoteCheckBox(Context context) {
    method NoteCheckBox (line 36) | public NoteCheckBox(Context context, AttributeSet attrs) {
    method NoteCheckBox (line 40) | public NoteCheckBox(Context context, AttributeSet attrs, int defStyle) {
    method getNoteId (line 44) | public long getNoteId() {
    method setNoteId (line 48) | public void setNoteId(long noteId) {

FILE: app/src/main/java/com/nononsenseapps/ui/NotificationItemHelper.java
  class NotificationItemHelper (line 42) | public final class NotificationItemHelper {
    method getDateString (line 44) | private static String getDateString(final Context context, final long ...
    method switchToTime (line 48) | private static void switchToTime(final View nv) {
    method showViews (line 58) | private static void showViews(final View... views) {
    method setTime (line 64) | private static void setTime(final Context context, final Notification ...
    method setup (line 86) | public static void setup(final TaskDetailFragment fragment,

FILE: app/src/main/java/com/nononsenseapps/ui/ShowcaseHelper.java
  class ShowcaseHelper (line 19) | public final class ShowcaseHelper {
    method showForOverflowMenu (line 26) | public static void showForOverflowMenu(@NonNull FragmentActivity activ...
    method finishConfiguringAndShow (line 50) | private static void finishConfiguringAndShow(TapTarget target, Fragmen...

FILE: app/src/main/java/com/nononsenseapps/ui/StyledEditText.java
  class StyledEditText (line 46) | public class StyledEditText extends AppCompatEditText {
    method StyledEditText (line 57) | public StyledEditText(Context context, AttributeSet attrs) {
    method setTitleRelativeLarger (line 111) | public void setTitleRelativeLarger(final boolean larger) {
    method setTitleFontFamily (line 119) | public void setTitleFontFamily(final int family) {
    method setTitleFontStyle (line 131) | public void setTitleFontStyle(final int style) {
    method setBodyFontFamily (line 142) | public void setBodyFontFamily(final int family) {
    method setTheTextSize (line 154) | public void setTheTextSize(final int size) {
    method setLinkify (line 171) | public void setLinkify(final boolean clickable) {
    method spannify (line 175) | private void spannify(final Spannable s) {
    method onTouchEvent (line 207) | @SuppressLint("ClickableViewAccessibility")

FILE: app/src/main/java/com/nononsenseapps/ui/TitleNoteTextView.java
  class TitleNoteTextView (line 51) | public class TitleNoteTextView extends AppCompatTextView {
    method TitleNoteTextView (line 68) | public TitleNoteTextView(Context context, AttributeSet attrs) {
    method getStyledText (line 115) | public static CharSequence getStyledText(final String title,
    method getStyledText (line 136) | public static CharSequence getStyledText(final String text,
    method setTitleFontFamily (line 168) | public void setTitleFontFamily(final int family) {
    method setTitleFontStyle (line 185) | public void setTitleFontStyle(final int style) {
    method setBodyFontFamily (line 201) | public void setBodyFontFamily(final int family) {
    method useSecondaryColor (line 215) | public void useSecondaryColor(final boolean useSecondary) {
    method getStyledText (line 225) | public String getStyledText() {
    method setStyledText (line 229) | public void setStyledText(final String styledText) {
    method onTouchEvent (line 283) | @SuppressLint("ClickableViewAccessibility")
    method onClickableSpanClicked (line 342) | private void onClickableSpanClicked(ClickableSpan cs) {
    method getTextRest (line 366) | public String getTextRest() {
    method setTextRest (line 370) | public void setTextRest(final String rest) {
    method getTextTitle (line 382) | public String getTextTitle() {
    method setTextTitle (line 386) | public void setTextTitle(final String title) {
    method setLinkify (line 399) | public void setLinkify(final boolean clickable) {
    method setTheTextSize (line 406) | public void setTheTextSize(final int size) {

FILE: app/src/main/java/com/nononsenseapps/ui/ViewsHelper.java
  class ViewsHelper (line 23) | public final class ViewsHelper {
    method convertDip2Pixels (line 28) | public static int convertDip2Pixels(Context context, int dip) {

FILE: app/src/main/java/com/nononsenseapps/ui/WeekDaysView.java
  class WeekDaysView (line 38) | public class WeekDaysView extends LinearLayout {
    type onCheckedDaysChangeListener (line 40) | public interface onCheckedDaysChangeListener {
      method onChange (line 47) | boolean onChange(long checkedDays);
    method WeekDaysView (line 74) | public WeekDaysView(Context context, AttributeSet attrs) {
    method initializeToggleButton (line 126) | void initializeToggleButton(final String text, final GreyableToggleBut...
    method getCheckedDays (line 133) | public long getCheckedDays() {
    method setCheckedDays (line 147) | public void setCheckedDays(long checkedDays) {
    method onDayButtonClicked (line 164) | private void onDayButtonClicked(View v) {
    method setOnCheckedDaysChangedListener (line 181) | public void setOnCheckedDaysChangedListener(onCheckedDaysChangeListene...

FILE: contract/src/main/java/com/nononsenseapps/notepad/providercontract/ProviderBase.java
  class ProviderBase (line 27) | public abstract class ProviderBase extends ContentProvider {
    method getAuthority (line 31) | protected abstract String getAuthority();
    method ProviderBase (line 33) | public ProviderBase() {
    method addContractUrisToMatcher (line 40) | private void addContractUrisToMatcher(@NonNull final UriMatcher uriMat...
    method query (line 44) | @Override
    method querySubItemsId (line 69) | protected abstract Cursor querySubItemsId(Uri uri, String[] projection...
    method querySubItems (line 72) | protected abstract Cursor querySubItems(Uri uri, String[] projection, ...
    method queryItemsId (line 75) | protected abstract Cursor queryItemsId(Uri uri, String[] projection, S...
    method queryItems (line 78) | protected abstract Cursor queryItems(Uri uri, String[] projection, Str...
    method queryAccountsId (line 81) | protected abstract Cursor queryAccountsId(Uri uri, String[] projection...
    method queryAccounts (line 84) | protected abstract Cursor queryAccounts(Uri uri, String[] projection, ...
    method queryFeatures (line 87) | protected abstract Cursor queryFeatures(Uri uri, String[] projection, ...
    method getType (line 90) | @Override
    method insert (line 95) | @Override
    method insertSubItem (line 111) | protected abstract Uri insertSubItem(Uri uri, ContentValues values);
    method insertItem (line 113) | protected abstract Uri insertItem(Uri uri, ContentValues values);
    method insertAccount (line 115) | protected abstract Uri insertAccount(Uri uri, ContentValues values);
    method delete (line 117) | @Override
    method deleteSubItemId (line 139) | protected abstract int deleteSubItemId(Uri uri, String selection, Stri...
    method deleteSubItem (line 141) | protected abstract int deleteSubItem(Uri uri, String selection, String...
    method deleteItemId (line 143) | protected abstract int deleteItemId(Uri uri, String selection, String[...
    method deleteItem (line 145) | protected abstract int deleteItem(Uri uri, String selection, String[] ...
    method deleteAccountsId (line 147) | protected abstract int deleteAccountsId(Uri uri, String selection, Str...
    method deleteAccounts (line 149) | protected abstract int deleteAccounts(Uri uri, String selection, Strin...
    method update (line 151) | @Override
    method moveSubItemId (line 178) | protected abstract int moveSubItemId(Uri uri, String selection, String...
    method updateSubItemId (line 180) | protected abstract int updateSubItemId(Uri uri, String selection, Stri...
    method updateSubItem (line 182) | protected abstract int updateSubItem(Uri uri, String selection, String...
    method moveItemId (line 184) | protected abstract int moveItemId(Uri uri, String selection, String[] ...
    method updateItemId (line 186) | protected abstract int updateItemId(Uri uri, String selection, String[...
    method updateItem (line 188) | protected abstract int updateItem(Uri uri, String selection, String[] ...
    method updateAccountsId (line 190) | protected abstract int updateAccountsId(Uri uri, String selection, Str...
    method updateAccounts (line 192) | protected abstract int updateAccounts(Uri uri, String selection, Strin...

FILE: contract/src/main/java/com/nononsenseapps/notepad/providercontract/ProviderContract.java
  class ProviderContract (line 30) | public class ProviderContract {
    method getTypeMask (line 78) | public static long getTypeMask(@TypeMask long... bitvalues) {
    method isType (line 93) | public static boolean isType(long bitmask, @TypeMask long type) {

FILE: contract/src/main/java/com/nononsenseapps/notepad/providercontract/UriContract.java
  class UriContract (line 10) | public class UriContract {
    method addUrisToMatcher (line 25) | public static void addUrisToMatcher(@NonNull final UriMatcher uriMatcher,

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortController.java
  class DragSortController (line 22) | public class DragSortController extends SimpleFloatViewManager implement...
    method DragSortController (line 92) | public DragSortController(DragSortListView dslv) {
    method DragSortController (line 96) | public DragSortController(DragSortListView dslv, int dragHandleId, int...
    method DragSortController (line 100) | public DragSortController(DragSortListView dslv, int dragHandleId, int...
    method DragSortController (line 111) | public DragSortController(DragSortListView dslv, int dragHandleId, int...
    method getDragInitMode (line 127) | public int getDragInitMode() {
    method setDragInitMode (line 137) | public void setDragInitMode(int mode) {
    method setSortEnabled (line 148) | public void setSortEnabled(boolean enabled) {
    method isSortEnabled (line 152) | public boolean isSortEnabled() {
    method setRemoveMode (line 161) | public void setRemoveMode(int mode) {
    method getRemoveMode (line 165) | public int getRemoveMode() {
    method setRemoveEnabled (line 172) | public void setRemoveEnabled(boolean enabled) {
    method isRemoveEnabled (line 176) | public boolean isRemoveEnabled() {
    method setDragHandleId (line 186) | public void setDragHandleId(int id) {
    method setFlingHandleId (line 196) | public void setFlingHandleId(int id) {
    method setClickRemoveId (line 206) | public void setClickRemoveId(int id) {
    method startDrag (line 220) | public boolean startDrag(int position, int deltaX, int deltaY) {
    method onTouch (line 236) | @Override
    method onDragFloatView (line 273) | @Override
    method startDragPosition (line 293) | public int startDragPosition(MotionEvent ev) {
    method startFlingPosition (line 297) | public int startFlingPosition(MotionEvent ev) {
    method dragHandleHitPosition (line 310) | public int dragHandleHitPosition(MotionEvent ev) {
    method flingHandleHitPosition (line 314) | public int flingHandleHitPosition(MotionEvent ev) {
    method viewIdHitPosition (line 318) | public int viewIdHitPosition(MotionEvent ev, int id) {
    method onDown (line 356) | @Override
    method onScroll (line 375) | @Override
    method onLongPress (line 409) | @Override
    method onFling (line 419) | @Override
    method onSingleTapUp (line 425) | @Override
    method onShowPress (line 436) | @Override
    method isDragging (line 441) | public boolean isDragging() {
    method onFling (line 447) | @Override

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortCursorAdapter.java
  class DragSortCursorAdapter (line 29) | public abstract class DragSortCursorAdapter extends CursorAdapter implem...
    method DragSortCursorAdapter (line 40) | @Deprecated
    method DragSortCursorAdapter (line 45) | public DragSortCursorAdapter(Context context, Cursor c, boolean autoRe...
    method DragSortCursorAdapter (line 49) | public DragSortCursorAdapter(Context context, Cursor c, int flags) {
    method swapCursor (line 58) | @Override
    method changeCursor (line 70) | @Override
    method reset (line 79) | public void reset() {
    method resetMappings (line 84) | private void resetMappings() {
    method getItem (line 89) | @Override
    method getItemId (line 94) | @Override
    method getDropDownView (line 99) | @Override
    method getView (line 104) | @Override
    method drop (line 116) | @Override
    method remove (line 144) | @Override
    method drag (line 165) | @Override
    method cleanMapping (line 173) | private void cleanMapping() {
    method getCount (line 188) | @Override
    method getCursorPosition (line 201) | public int getCursorPosition(int position) {
    method getCursorPositions (line 209) | public ArrayList<Integer> getCursorPositions() {
    method getListPosition (line 227) | public int getListPosition(int cursorPosition) {

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortItemView.java
  class DragSortItemView (line 26) | public class DragSortItemView extends ViewGroup {
    method DragSortItemView (line 30) | public DragSortItemView(Context context) {
    method setGravity (line 41) | public void setGravity(int gravity) {
    method getGravity (line 45) | public int getGravity() {
    method onLayout (line 49) | @Override
    method onMeasure (line 67) | @Override

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortItemViewCheckable.java
  class DragSortItemViewCheckable (line 27) | public class DragSortItemViewCheckable extends DragSortItemView implemen...
    method DragSortItemViewCheckable (line 29) | public DragSortItemViewCheckable(Context context) {
    method isChecked (line 33) | @Override
    method setChecked (line 42) | @Override
    method toggle (line 49) | @Override

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/DragSortListView.java
  class DragSortListView (line 58) | public class DragSortListView extends ListView implements OnTouchListener {
    method getSpeed (line 270) | @Override
    method DragSortListView (line 444) | public DragSortListView(Context context, AttributeSet attrs) {
    method setOnTouchListener (line 574) | @Override
    method onTouch (line 579) | @Override
    method setFloatAlpha (line 594) | public void setFloatAlpha(float alpha) {
    method getFloatAlpha (line 598) | public float getFloatAlpha() {
    method setMaxScrollSpeed (line 608) | public void setMaxScrollSpeed(float max) {
    method setAdapter (line 621) | @Override
    method getInputAdapter (line 650) | public ListAdapter getInputAdapter() {
    class AdapterWrapper (line 658) | private class AdapterWrapper extends BaseAdapter {
      method AdapterWrapper (line 661) | public AdapterWrapper(ListAdapter adapter) {
      method getAdapter (line 676) | public ListAdapter getAdapter() {
      method getItemId (line 680) | @Override
      method getItem (line 685) | @Override
      method getCount (line 690) | @Override
      method areAllItemsEnabled (line 695) | @Override
      method isEnabled (line 700) | @Override
      method getItemViewType (line 705) | @Override
      method getViewTypeCount (line 710) | @Override
      method hasStableIds (line 715) | @Override
      method isEmpty (line 720) | @Override
      method getView (line 726) | @Override
    method drawDivider (line 766) | private void drawDivider(int expPosition, Canvas canvas) {
    method dispatchDraw (line 802) | @Override
    method getItemHeight (line 849) | private int getItemHeight(int position) {
    method printPosData (line 862) | private void printPosData() {
    class HeightCache (line 867) | private class HeightCache {
      method HeightCache (line 873) | public HeightCache(int size) {
      method add (line 882) | public void add(int position, int height) {
      method get (line 899) | public int get(int position) {
      method clear (line 903) | public void clear() {
    method getShuffleEdge (line 929) | private int getShuffleEdge(int position, int top) {
    method updatePositions (line 988) | private boolean updatePositions() {
    method onDraw (line 1137) | @Override
    class SmoothAnimator (line 1146) | private class SmoothAnimator implements Runnable {
      method SmoothAnimator (line 1156) | public SmoothAnimator(float smoothness, int duration) {
      method transform (line 1164) | public float transform(float frac) {
      method start (line 1174) | public void start() {
      method cancel (line 1181) | public void cancel() {
      method onStart (line 1185) | public void onStart() {
      method onUpdate (line 1189) | public void onUpdate(float frac, float smoothFrac) {
      method onStop (line 1193) | public void onStop() {
      method run (line 1197) | @Override
    class LiftAnimator (line 1218) | private class LiftAnimator extends SmoothAnimator {
      method LiftAnimator (line 1223) | public LiftAnimator(float smoothness, int duration) {
      method onStart (line 1227) | @Override
      method onUpdate (line 1233) | @Override
    class DropAnimator (line 1249) | private class DropAnimator extends SmoothAnimator {
      method DropAnimator (line 1256) | public DropAnimator(float smoothness, int duration) {
      method onStart (line 1260) | @Override
      method getTargetY (line 1269) | private int getTargetY() {
      method onUpdate (line 1292) | @Override
      method onStop (line 1306) | @Override
    class RemoveAnimator (line 1316) | private class RemoveAnimator extends SmoothAnimator {
      method RemoveAnimator (line 1329) | public RemoveAnimator(float smoothness, int duration) {
      method onStart (line 1333) | @Override
      method onUpdate (line 1359) | @Override
      method onStop (line 1409) | @Override
    method removeItem (line 1415) | public void removeItem(int which) {
    method removeItem (line 1428) | public void removeItem(int which, float velocityX) {
    method moveItem (line 1474) | public void moveItem(int from, int to) {
    method cancelDrag (line 1487) | public void cancelDrag() {
    method clearPositions (line 1502) | private void clearPositions() {
    method dropFloatView (line 1509) | private void dropFloatView() {
    method doRemoveItem (line 1533) | private void doRemoveItem() {
    method doRemoveItem (line 1540) | private void doRemoveItem(int which) {
    method adjustOnReorder (line 1563) | private void adjustOnReorder() {
    method stopDrag (line 1589) | public boolean stopDrag(boolean remove) {
    method stopDragWithVelocity (line 1594) | public boolean stopDragWithVelocity(boolean remove, float velocityX) {
    method stopDrag (line 1600) | public boolean stopDrag(boolean remove, float velocityX) {
    method onTouchEvent (line 1625) | @Override
    method doActionUpOrCancel (line 1677) | private void doActionUpOrCancel() {
    method saveTouchCoords (line 1688) | private void saveTouchCoords(MotionEvent ev) {
    method listViewIntercepted (line 1704) | public boolean listViewIntercepted() {
    method onInterceptTouchEvent (line 1710) | @Override
    method setDragScrollStart (line 1770) | public void setDragScrollStart(float heightFraction) {
    method setDragScrollStarts (line 1783) | public void setDragScrollStarts(float upperFrac, float lowerFrac) {
    method continueDrag (line 1801) | private void continueDrag(int x, int y) {
    method updateScrollStarts (line 1846) | private void updateScrollStarts() {
    method onSizeChanged (line 1861) | @Override
    method adjustAllItems (line 1867) | private void adjustAllItems() {
    method adjustItem (line 1882) | private void adjustItem(int position) {
    method adjustItem (line 1894) | private void adjustItem(int position, View v, boolean invalidChildHeig...
    method getChildHeight (line 1933) | private int getChildHeight(int position) {
    method getChildHeight (line 1984) | private int getChildHeight(int position, View item, boolean invalidChi...
    method calcItemHeight (line 2014) | private int calcItemHeight(int position, View item, boolean invalidChi...
    method calcItemHeight (line 2018) | private int calcItemHeight(int position, int childHeight) {
    method requestLayout (line 2057) | @Override
    method adjustScroll (line 2064) | private int adjustScroll(int movePos, View moveItem, int oldFirstExpPo...
    method measureItem (line 2107) | private void measureItem(View item) {
    method measureFloatView (line 2124) | private void measureFloatView() {
    method onMeasure (line 2132) | @Override
    method layoutChildren (line 2145) | @Override
    method onDragTouchEvent (line 2161) | protected boolean onDragTouchEvent(MotionEvent ev) {
    method invalidateFloatView (line 2189) | private void invalidateFloatView() {
    method startDrag (line 2215) | public boolean startDrag(int position, int dragFlags, int deltaX, int ...
    method startDrag (line 2251) | public boolean startDrag(int position, View floatView, int dragFlags, ...
    method doDragFloatView (line 2314) | private void doDragFloatView(boolean forceInvalidate) {
    method doDragFloatView (line 2325) | private void doDragFloatView(int movePos, View moveItem, boolean force...
    method updateFloatView (line 2355) | private void updateFloatView() {
    method destroyFloatView (line 2416) | private void destroyFloatView() {
    type FloatViewManager (line 2434) | public interface FloatViewManager {
      method onCreateFloatView (line 2449) | public View onCreateFloatView(int position);
      method onDragFloatView (line 2466) | public void onDragFloatView(View floatView, Point location, Point to...
      method onDestroyFloatView (line 2476) | public void onDestroyFloatView(View floatView);
    method setFloatViewManager (line 2479) | public void setFloatViewManager(FloatViewManager manager) {
    method setDragListener (line 2483) | public void setDragListener(DragListener l) {
    method setDragEnabled (line 2497) | public void setDragEnabled(boolean enabled) {
    method isDragEnabled (line 2501) | public boolean isDragEnabled() {
    method setDropListener (line 2517) | public void setDropListener(DropListener l) {
    method setRemoveListener (line 2531) | public void setRemoveListener(RemoveListener l) {
    type DragListener (line 2535) | public interface DragListener {
      method drag (line 2536) | public void drag(int from, int to);
    type DropListener (line 2547) | public interface DropListener {
      method drop (line 2548) | public void drop(int from, int to);
    type RemoveListener (line 2558) | public interface RemoveListener {
      method remove (line 2559) | public void remove(int which);
    type DragSortListener (line 2562) | public interface DragSortListener extends DropListener, DragListener, ...
    method setDragSortListener (line 2565) | public void setDragSortListener(DragSortListener l) {
    method setDragScrollProfile (line 2578) | public void setDragScrollProfile(DragScrollProfile ssp) {
    method moveCheckState (line 2610) | public void moveCheckState(int from, int to) {
    method removeCheckState (line 2682) | public void removeCheckState(int position) {
    method buildRunList (line 2703) | private static int buildRunList(SparseBooleanArray cip, int rangeStart,
    method rotate (line 2752) | private static int rotate(int value, int offset, int lowerBound, int u...
    method findFirstSetIndex (line 2764) | private static int findFirstSetIndex(SparseBooleanArray sba, int range...
    method insertionIndexForKey (line 2774) | private static int insertionIndexForKey(SparseBooleanArray sba, int ke...
    type DragScrollProfile (line 2795) | public interface DragScrollProfile {
      method getSpeed (line 2806) | float getSpeed(float w, long t);
    class DragScroller (line 2809) | private class DragScroller implements Runnable {
      method isScrolling (line 2832) | public boolean isScrolling() {
      method getScrollDir (line 2836) | public int getScrollDir() {
      method DragScroller (line 2840) | public DragScroller() {
      method startScrolling (line 2843) | public void startScrolling(int dir) {
      method stopScrolling (line 2855) | public void stopScrolling(boolean now) {
      method run (line 2866) | @Override
    class DragSortTracker (line 2957) | private class DragSortTracker {
      method DragSortTracker (line 2967) | public DragSortTracker() {
      method startTracking (line 2983) | public void startTracking() {
      method appendState (line 2989) | public void appendState() {
      method flush (line 3043) | public void flush() {
      method stopTracking (line 3068) | public void stopTracking() {

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/ResourceDragSortCursorAdapter.java
  class ResourceDragSortCursorAdapter (line 34) | public abstract class ResourceDragSortCursorAdapter extends DragSortCurs...
    method ResourceDragSortCursorAdapter (line 53) | @Deprecated
    method ResourceDragSortCursorAdapter (line 76) | public ResourceDragSortCursorAdapter(Context context, int layout, Curs...
    method ResourceDragSortCursorAdapter (line 93) | public ResourceDragSortCursorAdapter(Context context, int layout, Curs...
    method newView (line 105) | @Override
    method newDropDownView (line 110) | @Override
    method setViewResource (line 120) | public void setViewResource(int layout) {
    method setDropDownViewResource (line 129) | public void setDropDownViewResource(int dropDownLayout) {

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/SimpleDragSortCursorAdapter.java
  class SimpleDragSortCursorAdapter (line 52) | public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorA...
    method SimpleDragSortCursorAdapter (line 80) | @Deprecated
    method SimpleDragSortCursorAdapter (line 106) | public SimpleDragSortCursorAdapter(Context context, int layout,
    method bindView (line 137) | @Override
    method getViewBinder (line 178) | public ViewBinder getViewBinder() {
    method setViewBinder (line 190) | public void setViewBinder(ViewBinder viewBinder) {
    method setViewImage (line 209) | public void setViewImage(ImageView v, String value) {
    method setViewText (line 228) | public void setViewText(TextView v, String text) {
    method getStringConversionColumn (line 242) | public int getStringConversionColumn() {
    method setStringConversionColumn (line 259) | public void setStringConversionColumn(int stringConversionColumn) {
    method getCursorToStringConverter (line 274) | public CursorToStringConverter getCursorToStringConverter() {
    method setCursorToStringConverter (line 289) | public void setCursorToStringConverter(CursorToStringConverter cursorT...
    method convertToString (line 303) | @Override
    method findColumns (line 321) | private void findColumns(Cursor c, String[] from) {
    method swapCursor (line 336) | @Override
    method changeCursorAndColumns (line 356) | public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
    type ViewBinder (line 379) | public static interface ViewBinder {
      method setViewValue (line 392) | boolean setViewValue(View view, Cursor cursor, int columnIndex);
    type CursorToStringConverter (line 401) | public static interface CursorToStringConverter {
      method convertToString (line 409) | CharSequence convertToString(Cursor cursor);

FILE: external/drag-sort-listview/src/com/mobeta/android/dslv/SimpleFloatViewManager.java
  class SimpleFloatViewManager (line 17) | public class SimpleFloatViewManager implements DragSortListView.FloatVie...
    method SimpleFloatViewManager (line 27) | public SimpleFloatViewManager(ListView lv) {
    method setBackgroundColor (line 31) | public void setBackgroundColor(int color) {
    method onCreateFloatView (line 39) | @Override
    method onDragFloatView (line 105) | @Override
    method onDestroyFloatView (line 114) | @Override
Condensed preview — 527 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,177K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 373,
    "preview": "# multiple github accounts are allowed, but only one of everything else. Setup is explained in:\n# https://docs.github.co"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 771,
    "preview": "---\nname: Bug report\nabout: Report issues of the app's features\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n### Describe t"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 598,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for the app\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/notifications_do_not_appear.md",
    "chars": 506,
    "preview": "---\nname: Notifications don't show up\nabout: The app does not show notifications for reminders\ntitle: ''\nlabels: bug\nass"
  },
  {
    "path": ".github/workflows/android_build.yml",
    "chars": 1316,
    "preview": "name: Android build\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n\njobs:\n  # this j"
  },
  {
    "path": ".github/workflows/android_tests.yml",
    "chars": 4935,
    "preview": "name: Android tests\n\non:\n  push:\n    branches: [ \"master\" ]\n    # runs when you commit to the master branch and when mer"
  },
  {
    "path": ".github/workflows/publish_playstore.yml",
    "chars": 1141,
    "preview": "name: Publish to Play store\n\non:\n  push:\n    # Branch only during testing\n    #branches:\n    #  - master\n    tags:\n     "
  },
  {
    "path": ".gitignore",
    "chars": 228,
    "preview": "secret.properties\n_release_keys_\nNotePad_ant\nNotePad_ant/*\n.factorypath\nbin/\nbuild/\n.gradle\ngen/\nNotePad.apk\nNotes_for_I"
  },
  {
    "path": "LICENSE",
    "chars": 35148,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "README.md",
    "chars": 3733,
    "preview": "# NoNonsense Notes\n\n[![Android build](https://github.com/spacecowboy/NotePad/actions/workflows/android_build.yml/badge.s"
  },
  {
    "path": "app/build.gradle",
    "chars": 9370,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DBItem.java",
    "chars": 1001,
    "preview": "package com.nononsenseapps.notepad.database;\n\nimport android.content.Context;\nimport android.content.ContentValues;\nimpo"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DatabaseHandler.java",
    "chars": 4413,
    "preview": "package com.nononsenseapps.notepad.database;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.content"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DatabaseTriggers.java",
    "chars": 427,
    "preview": "package com.nononsenseapps.notepad.database;\n\nimport android.database.sqlite.SQLiteDatabase;\n\npublic class DatabaseTrigg"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/DatabaseViews.java",
    "chars": 615,
    "preview": "package com.nononsenseapps.notepad.database;\n\nimport android.database.sqlite.SQLiteDatabase;\n\npublic class DatabaseViews"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/ItemProvider.java",
    "chars": 3669,
    "preview": "package com.nononsenseapps.notepad.database;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValu"
  },
  {
    "path": "app/dbgenout/com/nononsenseapps/notepad/database/taskviewItem.java",
    "chars": 3385,
    "preview": "package com.nononsenseapps.notepad.database;\n\nimport android.content.ContentValues;\nimport android.content.UriMatcher;\ni"
  },
  {
    "path": "app/dbsetup.py",
    "chars": 3967,
    "preview": "\"\"\"Generate a sample project with triggers\"\"\"\n\nfrom AndroidCodeGenerator.generator import Generator\nfrom AndroidCodeGene"
  },
  {
    "path": "app/proguard.pro",
    "chars": 724,
    "preview": "# Add project specific ProGuard rules here.\n# You can edit the include path and order by changing the proguardFiles\n# di"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/BaseTestClass.java",
    "chars": 4108,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/EspressoHelper.java",
    "chars": 7718,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddBigNumberOfNotesScrollDownAndDeleteOne.java",
    "chars": 3196,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static android.view.View.FIND_VIEWS_WITH_TEXT;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteShouldShowNameInNotesScreen.java",
    "chars": 934,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithDueDateCheckDateIsVisible.java",
    "chars": 1317,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNewNoteWithReminderDateAndTime.java",
    "chars": 1736,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNoteToTaskList.java",
    "chars": 1634,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddNotesAndRotateScreen.java",
    "chars": 1876,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListAndRotateScreen.java",
    "chars": 1458,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListCheckItIsAddedToDrawer.java",
    "chars": 1075,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestAddTaskListsScrollNavigationDrawer.java",
    "chars": 2302,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onData;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCompletedTasksAreCleared.java",
    "chars": 2655,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onData;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateNoteAndDeleteIt.java",
    "chars": 1463,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestCreateTaskListAndDeleteIt.java",
    "chars": 1582,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestPasswords.java",
    "chars": 2817,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/espresso_tests/TestSaveLoadJsonBackup.java",
    "chars": 5296,
    "preview": "package com.nononsenseapps.notepad.espresso_tests;\n\nimport static androidx.test.espresso.Espresso.onData;\nimport static "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBFreshTest.java",
    "chars": 1873,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderMovementTest.java",
    "chars": 18992,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport android.content.ContentResolver;\nimport android.content.ContentValues;\n"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBProviderTest.java",
    "chars": 4676,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DBUpgradeTest.java",
    "chars": 10539,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport a"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DaoTaskTest.java",
    "chars": 2858,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertNotEquals;\n\nimport androidx.test.filters."
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DashClockSettingsTest.java",
    "chars": 1318,
    "preview": "/*\n * Copyright (c) 2014 Jonas Kalderstam.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you "
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/DateTimeTest.java",
    "chars": 581,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.nononsenseapps.notepad.dat"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskDetailTest.java",
    "chars": 927,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.a"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsTest.java",
    "chars": 1665,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.as"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/FragmentTaskListsViewPagerTest.java",
    "chars": 875,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.as"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/Helper.java",
    "chars": 1995,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/OrgSyncTest.java",
    "chars": 22731,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.as"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/ProviderHelperTest.java",
    "chars": 3819,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/RFCDateTest.java",
    "chars": 2456,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport android.util.Log;\n\nimport com.nononsenseapps.helpers.RFC3339Date;\n\nimpo"
  },
  {
    "path": "app/src/androidTest/java/com/nononsenseapps/notepad/test/StorageTest.java",
    "chars": 687,
    "preview": "package com.nononsenseapps.notepad.test;\n\nimport androidx.test.platform.app.InstrumentationRegistry;\n\nimport com.nononse"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 13672,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you"
  },
  {
    "path": "app/src/main/assets/.gitignore",
    "chars": 22,
    "preview": "secretkeys.properties\n"
  },
  {
    "path": "app/src/main/java/com/google/android/apps/dashclock/ui/DragGripView.java",
    "chars": 3869,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/ActivityHelper.java",
    "chars": 3220,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/DocumentFileHelper.java",
    "chars": 4636,
    "preview": "package com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.net.Uri;\nimport android.os.ParcelFil"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/FileHelper.java",
    "chars": 3244,
    "preview": "package com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.media.MediaScannerConnection;\nimport"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/FilePickerHelper.java",
    "chars": 3609,
    "preview": "package com.nononsenseapps.helpers;\n\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\ni"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/ListHelper.java",
    "chars": 4597,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/NnnLogger.java",
    "chars": 3225,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/NotificationHelper.java",
    "chars": 25675,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/PermissionsHelper.java",
    "chars": 2130,
    "preview": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/PreferencesHelper.java",
    "chars": 4408,
    "preview": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/RFC3339Date.java",
    "chars": 4818,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/SyncStatusMonitor.java",
    "chars": 3930,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/ThemeHelper.java",
    "chars": 1986,
    "preview": "package com.nononsenseapps.helpers;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport an"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/TimeFormatter.java",
    "chars": 10231,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/helpers/UpdateNotifier.java",
    "chars": 4138,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/NnnApp.java",
    "chars": 1537,
    "preview": "package com.nononsenseapps.notepad;\n\nimport android.app.Application;\nimport android.os.Build;\nimport android.os.StrictMo"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/NotePadBroadcastReceiver.java",
    "chars": 2269,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/ActivitySearch.java",
    "chars": 3783,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/ActivitySearchDeleted.java",
    "chars": 1019,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/ActivityTaskHistory.java",
    "chars": 5945,
    "preview": "package com.nononsenseapps.notepad.activities;\n\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\ni"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/main/ActivityMain.java",
    "chars": 32618,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/main/ActivityMainHelper.java",
    "chars": 5028,
    "preview": "package com.nononsenseapps.notepad.activities.main;\n\nimport android.content.Context;\nimport android.content.Intent;\n\nimp"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/activities/main/DrawerCursorLoader.java",
    "chars": 3404,
    "preview": "package com.nononsenseapps.notepad.activities.main;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimp"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/DummyProvider.java",
    "chars": 10638,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/ProviderHelper.java",
    "chars": 5309,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/ProviderManager.java",
    "chars": 5354,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/android/provider/TextFileProvider.java",
    "chars": 5475,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/dashclock/DashclockPrefActivity.java",
    "chars": 1655,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/dashclock/DashclockPrefsFragment.java",
    "chars": 4734,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/dashclock/TasksExtension.java",
    "chars": 7651,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/DAO.java",
    "chars": 9172,
    "preview": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/DatabaseHandler.java",
    "chars": 12135,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/LegacyDBHelper.java",
    "chars": 27736,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/MyContentProvider.java",
    "chars": 23088,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/Notification.java",
    "chars": 22798,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/RemoteTask.java",
    "chars": 10090,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/RemoteTaskList.java",
    "chars": 8708,
    "preview": "/*\n * Copyright (c) 2015. Jonas Kalderstam\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/Task.java",
    "chars": 45775,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/database/TaskList.java",
    "chars": 6890,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogConfirmBase.java",
    "chars": 1708,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogConfirmBaseV11.java",
    "chars": 1716,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteCompletedTasks.java",
    "chars": 2514,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteList.java",
    "chars": 1923,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogDeleteTask.java",
    "chars": 1955,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogEditList.java",
    "chars": 9521,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogExportBackup.java",
    "chars": 1841,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogMoveToList.java",
    "chars": 5835,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogPassword.java",
    "chars": 5102,
    "preview": "/*\n * Copyright (C) 2012 Jonas Kalderstam\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may "
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogPasswordV11.java",
    "chars": 3814,
    "preview": "/*\n * Copyright (C) 2012 Jonas Kalderstam\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you m"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogRestore.java",
    "chars": 3900,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/DialogRestoreBackup.java",
    "chars": 2127,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/FragmentSearch.java",
    "chars": 8715,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/FragmentSearchDeleted.java",
    "chars": 9513,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/TaskDetailFragment.java",
    "chars": 32392,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/TaskListFragment.java",
    "chars": 29726,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/fragments/TaskListViewPagerFragment.java",
    "chars": 14424,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/interfaces/ListOpener.java",
    "chars": 344,
    "preview": "package com.nononsenseapps.notepad.interfaces;\n\nimport com.nononsenseapps.notepad.activities.main.ActivityMain;\nimport c"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/interfaces/MenuStateController.java",
    "chars": 1008,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/interfaces/OnFragmentInteractionListener.java",
    "chars": 605,
    "preview": "package com.nononsenseapps.notepad.interfaces;\n\nimport android.net.Uri;\nimport android.view.View;\n\nimport androidx.fragm"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/AboutPrefs.java",
    "chars": 1913,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/AppearancePrefs.java",
    "chars": 5272,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/BackupPrefs.java",
    "chars": 6929,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/Constants.java",
    "chars": 1332,
    "preview": "package com.nononsenseapps.notepad.prefs;\n\n/**\n * Key names of the preferences.\n */\npublic final class Constants {\n\n\t// "
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/IndexPrefs.java",
    "chars": 486,
    "preview": "package com.nononsenseapps.notepad.prefs;\n\n\nimport android.os.Bundle;\n\nimport androidx.annotation.Nullable;\nimport andro"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/ListPrefs.java",
    "chars": 4459,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/NotificationPrefs.java",
    "chars": 12773,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/PasswordPrefs.java",
    "chars": 4423,
    "preview": "/*\n * Copyright (C) 2012 Jonas Kalderstam\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you m"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/PrefsActivity.java",
    "chars": 7717,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/prefs/SyncPrefs.java",
    "chars": 6773,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/SyncAdapter.java",
    "chars": 1269,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/files/JSONBackup.java",
    "chars": 11785,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/googleapi/GoogleTask.java",
    "chars": 2892,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/googleapi/GoogleTaskList.java",
    "chars": 1797,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/BackgroundSyncScheduler.java",
    "chars": 2522,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/DBSyncBase.java",
    "chars": 12447,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/Monitor.java",
    "chars": 1208,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgConverter.java",
    "chars": 9492,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgProvider.java",
    "chars": 6681,
    "preview": "package com.nononsenseapps.notepad.sync.orgsync;\n\nimport android.content.ContentProvider;\nimport android.content.Content"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/OrgSyncService.java",
    "chars": 8496,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/RemoteTaskListFile.java",
    "chars": 1235,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/RemoteTaskNode.java",
    "chars": 1581,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/SDSynchronizer.java",
    "chars": 8167,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/Synchronizer.java",
    "chars": 13417,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/sync/orgsync/SynchronizerInterface.java",
    "chars": 3201,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetConfig.java",
    "chars": 25339,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetProvider.java",
    "chars": 11713,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/ListWidgetService.java",
    "chars": 12206,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/list/WidgetPrefs.java",
    "chars": 4047,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/notepad/widget/shortcut/ShortcutConfig.java",
    "chars": 7024,
    "preview": "package com.nononsenseapps.notepad.widget.shortcut;\n\nimport android.content.Intent;\nimport android.content.pm.ShortcutIn"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/DateView.java",
    "chars": 2646,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/DelegateFrame.java",
    "chars": 3072,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ExtraTypesCursorAdapter.java",
    "chars": 3081,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ExtrasCursorAdapter.java",
    "chars": 6534,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/GreyableToggleButton.java",
    "chars": 2396,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/NoteCheckBox.java",
    "chars": 1446,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/NotificationItemHelper.java",
    "chars": 6641,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ShowcaseHelper.java",
    "chars": 3321,
    "preview": "package com.nononsenseapps.ui;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotatio"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/StyledEditText.java",
    "chars": 7386,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/TitleNoteTextView.java",
    "chars": 12573,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/ViewsHelper.java",
    "chars": 1089,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/java/com/nononsenseapps/ui/WeekDaysView.java",
    "chars": 6269,
    "preview": "/*\n * Copyright (c) 2015 Jonas Kalderstam.\n *\n * This program is free software: you can redistribute it and/or modify\n *"
  },
  {
    "path": "app/src/main/res/anim/activity_slide_in_right.xml",
    "chars": 813,
    "preview": "<!--\n Copyright (C) 2013 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/res/anim/activity_slide_out_right_full.xml",
    "chars": 813,
    "preview": "<!--\n Copyright (C) 2013 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/res/anim/cycle_7.xml",
    "chars": 765,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache Lic"
  },
  {
    "path": "app/src/main/res/anim/shake.xml",
    "chars": 971,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2012 Jonas Kalderstam\n\n     Licensed under the Apache Lic"
  },
  {
    "path": "app/src/main/res/anim/slide_in_bottom.xml",
    "chars": 351,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate andr"
  },
  {
    "path": "app/src/main/res/anim/slide_in_top.xml",
    "chars": 352,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate andr"
  },
  {
    "path": "app/src/main/res/anim/slide_out_bottom.xml",
    "chars": 351,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate andr"
  },
  {
    "path": "app/src/main/res/anim/slide_out_top.xml",
    "chars": 352,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<translate andr"
  },
  {
    "path": "app/src/main/res/drawable/btn_toggle_bg.xml",
    "chars": 1686,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright (c) 2014 Jonas Kalderstam.\n\n  Licensed under the Apache License,"
  },
  {
    "path": "app/src/main/res/drawable/folder_move_24dp.xml",
    "chars": 1079,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/folder_plus_24dp.xml",
    "chars": 1096,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_add_24dp.xml",
    "chars": 961,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_alarm_24dp.xml",
    "chars": 1206,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_alarm_add_24dp.xml",
    "chars": 1196,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_archive_24dp.xml",
    "chars": 369,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:heigh"
  },
  {
    "path": "app/src/main/res/drawable/ic_brightness_6_24dp.xml",
    "chars": 1074,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_check_24dp.xml",
    "chars": 976,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_checkbox_checked.xml",
    "chars": 409,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:heigh"
  },
  {
    "path": "app/src/main/res/drawable/ic_checkbox_unchecked.xml",
    "chars": 377,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:heigh"
  },
  {
    "path": "app/src/main/res/drawable/ic_clear_24dp.xml",
    "chars": 1027,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_copy_24dp.xml",
    "chars": 361,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid"
  },
  {
    "path": "app/src/main/res/drawable/ic_delete_24dp.xml",
    "chars": 1009,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_export.xml",
    "chars": 434,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid"
  },
  {
    "path": "app/src/main/res/drawable/ic_folder_24dp.xml",
    "chars": 1033,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_help_24dp.xml",
    "chars": 1233,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_import.xml",
    "chars": 413,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid"
  },
  {
    "path": "app/src/main/res/drawable/ic_info_24dp.xml",
    "chars": 1025,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_lock_closed_24dp.xml",
    "chars": 1208,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_lock_open_24dp.xml",
    "chars": 1191,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_notebook_minus_24dp.xml",
    "chars": 523,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:heigh"
  },
  {
    "path": "app/src/main/res/drawable/ic_refresh_24dp.xml",
    "chars": 1144,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_sd_storage_24dp.xml",
    "chars": 1050,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_search_24dp.xml",
    "chars": 1172,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_select_all.xml",
    "chars": 516,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid"
  },
  {
    "path": "app/src/main/res/drawable/ic_settings_24dp.xml",
    "chars": 1797,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_share_24dp.xml",
    "chars": 1357,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_sort_24dp.xml",
    "chars": 970,
    "preview": "<!--\n  Copyright (c) 2015. Jonas Kalderstam\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_stat_notification_edit.xml",
    "chars": 1278,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/ic_underline_accent.xml",
    "chars": 747,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:height=\"24dp\"\n\tandroid:width=\"24dp\"\n\tandroid"
  },
  {
    "path": "app/src/main/res/drawable/ic_undo_24dp.xml",
    "chars": 1081,
    "preview": "<!--\n  Copyright (c) 2015 Jonas Kalderstam.\n\n  This program is free software: you can redistribute it and/or modify\n  it"
  },
  {
    "path": "app/src/main/res/drawable/img_default_selector_dark.xml",
    "chars": 1105,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2010 The Android Open Source Project\n\n     Licensed under"
  },
  {
    "path": "app/src/main/res/drawable/img_default_selector_light.xml",
    "chars": 1016,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2010 The Android Open Source Project\n\n     Licensed under"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_blackclassic_bg.xml",
    "chars": 260,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item and"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_darkcard_bg.xml",
    "chars": 555,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<!-- andr"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_lightcard_bg.xml",
    "chars": 260,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item and"
  },
  {
    "path": "app/src/main/res/drawable/tasklist_item_lightclassic_bg.xml",
    "chars": 268,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item and"
  },
  {
    "path": "app/src/main/res/drawable-anydpi-v26/app_icon.xml",
    "chars": 500,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<!-- "
  }
]

// ... and 327 more files (download for full content)

About this extraction

This page contains the full source code of the spacecowboy/NotePad GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 527 files (1.8 MB), approximately 517.7k tokens, and a symbol index with 1574 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!