├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── dependabot.yml └── workflows │ ├── android.yml │ └── codeql.yml ├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── deploymentTargetSelector.xml ├── migrations.xml ├── misc.xml └── runConfigurations.xml ├── .tx └── config ├── CONTRIBUTING.md ├── LICENSE ├── PRIVACY.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── it │ │ │ └── niedermann │ │ │ └── nextcloud │ │ │ └── tables │ │ │ ├── TablesApplication.java │ │ │ ├── features │ │ │ ├── about │ │ │ │ ├── AboutActivity.java │ │ │ │ ├── AboutFragmentContributingTab.java │ │ │ │ ├── AboutFragmentCreditsTab.java │ │ │ │ ├── AboutFragmentLicenseTab.java │ │ │ │ └── AboutViewModel.java │ │ │ ├── accountswitcher │ │ │ │ ├── AccountSwitcherAdapter.java │ │ │ │ ├── AccountSwitcherDialog.java │ │ │ │ ├── AccountSwitcherViewHolder.java │ │ │ │ └── AccountViewModel.java │ │ │ ├── column │ │ │ │ ├── edit │ │ │ │ │ ├── EDataTypePicker.java │ │ │ │ │ ├── EditColumnActivity.java │ │ │ │ │ ├── EditColumnViewModel.java │ │ │ │ │ ├── SearchProviderSupplier.java │ │ │ │ │ └── types │ │ │ │ │ │ ├── ColumnEditView.java │ │ │ │ │ │ ├── ColumnEditViewFactory.java │ │ │ │ │ │ ├── datetime │ │ │ │ │ │ ├── DateManager.java │ │ │ │ │ │ └── DateTimeManager.java │ │ │ │ │ │ ├── number │ │ │ │ │ │ ├── NumberManager.java │ │ │ │ │ │ ├── ProgressManager.java │ │ │ │ │ │ └── StarsManager.java │ │ │ │ │ │ ├── selection │ │ │ │ │ │ ├── SelectionCheckManager.java │ │ │ │ │ │ ├── SelectionMultiManager.java │ │ │ │ │ │ └── SelectionSingleManager.java │ │ │ │ │ │ ├── text │ │ │ │ │ │ ├── TextLineManager.java │ │ │ │ │ │ ├── TextLinkManager.java │ │ │ │ │ │ └── TextRichManager.java │ │ │ │ │ │ ├── unknown │ │ │ │ │ │ └── UnknownManager.java │ │ │ │ │ │ └── usergroup │ │ │ │ │ │ └── UserGroupManager.java │ │ │ │ └── manage │ │ │ │ │ ├── ManageColumnsActivity.java │ │ │ │ │ ├── ManageColumnsAdapter.java │ │ │ │ │ ├── ManageColumnsTouchHelper.java │ │ │ │ │ ├── ManageColumnsViewHolder.java │ │ │ │ │ └── ManageColumnsViewModel.java │ │ │ ├── exception │ │ │ │ ├── ExceptionActivity.java │ │ │ │ ├── ExceptionDialogFragment.java │ │ │ │ ├── ExceptionHandler.java │ │ │ │ └── tips │ │ │ │ │ ├── TipsAdapter.java │ │ │ │ │ ├── TipsModel.java │ │ │ │ │ └── TipsViewHolder.java │ │ │ ├── importaccount │ │ │ │ ├── ImportAccountActivity.java │ │ │ │ └── ImportAccountViewModel.java │ │ │ ├── main │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MainViewModel.java │ │ │ │ └── TableContextPopupMenu.java │ │ │ ├── manageaccounts │ │ │ │ ├── ManageAccountAdapter.java │ │ │ │ ├── ManageAccountViewHolder.java │ │ │ │ ├── ManageAccountsActivity.java │ │ │ │ └── ManageAccountsViewModel.java │ │ │ ├── row │ │ │ │ ├── EditRowActivity.java │ │ │ │ ├── EditRowViewModel.java │ │ │ │ └── editor │ │ │ │ │ ├── OnTextChangedListener.java │ │ │ │ │ ├── ProposalProvider.java │ │ │ │ │ └── type │ │ │ │ │ ├── AutocompleteEditView.java │ │ │ │ │ ├── AutocompleteEditViewWithDefaultDropdown.java │ │ │ │ │ ├── DataEditView.java │ │ │ │ │ ├── DataEditViewFactory.java │ │ │ │ │ ├── datetime │ │ │ │ │ ├── DateTimeDateEditor.java │ │ │ │ │ ├── DateTimeEditor.java │ │ │ │ │ └── DateTimeTimeEditor.java │ │ │ │ │ ├── number │ │ │ │ │ ├── NumberEditor.java │ │ │ │ │ ├── NumberProgressEditor.java │ │ │ │ │ └── NumberStarsEditor.java │ │ │ │ │ ├── selection │ │ │ │ │ ├── SelectionCheckEditor.java │ │ │ │ │ ├── SelectionEditor.java │ │ │ │ │ └── SelectionMultiEditor.java │ │ │ │ │ ├── text │ │ │ │ │ ├── TextEditor.java │ │ │ │ │ ├── TextLineEditor.java │ │ │ │ │ ├── TextLinkEditor.java │ │ │ │ │ └── TextRichEditor.java │ │ │ │ │ ├── unknown │ │ │ │ │ └── UnknownEditor.java │ │ │ │ │ └── usergroup │ │ │ │ │ └── UserGroupEditor.java │ │ │ ├── settings │ │ │ │ ├── PreferencesActivity.java │ │ │ │ └── PreferencesFragment.java │ │ │ └── table │ │ │ │ ├── edit │ │ │ │ ├── EditTableActivity.java │ │ │ │ └── EditTableViewModel.java │ │ │ │ └── view │ │ │ │ ├── DefaultTableViewListener.java │ │ │ │ ├── TableViewAdapter.java │ │ │ │ ├── ViewTableFragment.java │ │ │ │ ├── ViewTableViewModel.java │ │ │ │ └── viewholder │ │ │ │ ├── CellViewHolder.java │ │ │ │ ├── CellViewHolderFactory.java │ │ │ │ ├── ColumnHeaderViewHolder.java │ │ │ │ ├── RowHeaderViewHolder.java │ │ │ │ ├── ViewHolderFactory.java │ │ │ │ └── types │ │ │ │ ├── datetime │ │ │ │ ├── AbstractDateTimeCellViewHolder.java │ │ │ │ ├── DateCellViewHolder.java │ │ │ │ ├── DateTimeCellViewHolder.java │ │ │ │ └── TimeCellViewHolder.java │ │ │ │ ├── number │ │ │ │ ├── NumberCellViewHolder.java │ │ │ │ ├── ProgressCellViewHolder.java │ │ │ │ └── StarsCellViewHolder.java │ │ │ │ ├── selection │ │ │ │ ├── SelectionCheckCellViewHolder.java │ │ │ │ ├── SelectionMultiViewHolder.java │ │ │ │ └── SelectionViewHolder.java │ │ │ │ ├── text │ │ │ │ ├── LinkCellViewHolder.java │ │ │ │ ├── LongCellViewHolder.java │ │ │ │ ├── RichViewHolder.java │ │ │ │ └── TextCellViewHolder.java │ │ │ │ └── usergroup │ │ │ │ └── UserGroupViewHolder.java │ │ │ └── util │ │ │ ├── AvatarUtil.java │ │ │ ├── CustomAppGlideModule.java │ │ │ ├── DimensionUtil.java │ │ │ ├── EmojiDrawable.java │ │ │ ├── SpannableUtil.java │ │ │ ├── TextLinkUtil.java │ │ │ └── UserGroupUtil.java │ └── res │ │ ├── drawable │ │ ├── app_icon.xml │ │ ├── baseline_access_time_24.xml │ │ ├── baseline_calendar_today_24.xml │ │ ├── baseline_edit_24.xml │ │ ├── baseline_link_24.xml │ │ ├── baseline_numbers_24.xml │ │ ├── baseline_playlist_add_24.xml │ │ ├── baseline_post_add_24.xml │ │ ├── baseline_question_mark_24.xml │ │ ├── baseline_short_text_24.xml │ │ ├── baseline_warning_amber_24.xml │ │ ├── circle_alpha_check_36dp.xml │ │ ├── ic_baseline_account_circle_24.xml │ │ ├── ic_baseline_add_24.xml │ │ ├── ic_baseline_arrow_back_24.xml │ │ ├── ic_baseline_brightness_2_24.xml │ │ ├── ic_baseline_check_24.xml │ │ ├── ic_baseline_delete_24.xml │ │ ├── ic_baseline_network_wifi_24.xml │ │ ├── ic_baseline_person_24.xml │ │ ├── ic_baseline_person_add_24.xml │ │ ├── ic_baseline_settings_24.xml │ │ ├── ic_baseline_sync_24.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_menu.xml │ │ ├── ic_outline_info_24.xml │ │ ├── ic_outline_lightbulb_24.xml │ │ ├── selected_check.xml │ │ └── shape_circular.xml │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_edit_column.xml │ │ ├── activity_edit_row.xml │ │ ├── activity_edit_table.xml │ │ ├── activity_exception.xml │ │ ├── activity_import.xml │ │ ├── activity_main.xml │ │ ├── activity_manage_accounts.xml │ │ ├── activity_manage_columns.xml │ │ ├── activity_preferences.xml │ │ ├── dialog_account_switcher.xml │ │ ├── dialog_exception.xml │ │ ├── edit_autocomplete.xml │ │ ├── edit_datetime.xml │ │ ├── edit_number_progress.xml │ │ ├── edit_number_stars.xml │ │ ├── edit_rich.xml │ │ ├── edit_selection.xml │ │ ├── edit_selection_check.xml │ │ ├── edit_selection_multi.xml │ │ ├── edit_textview.xml │ │ ├── fragment_about_contribution_tab.xml │ │ ├── fragment_about_credits_tab.xml │ │ ├── fragment_about_license_tab.xml │ │ ├── fragment_table.xml │ │ ├── item_account_and_version.xml │ │ ├── item_account_choose.xml │ │ ├── item_autocomplete.xml │ │ ├── item_column.xml │ │ ├── item_manage_option_multi.xml │ │ ├── item_manage_option_single.xml │ │ ├── item_option.xml │ │ ├── item_row.xml │ │ ├── item_tip.xml │ │ ├── manage_date.xml │ │ ├── manage_datetime.xml │ │ ├── manage_number.xml │ │ ├── manage_number_progress.xml │ │ ├── manage_number_stars.xml │ │ ├── manage_selection_check.xml │ │ ├── manage_selection_multi.xml │ │ ├── manage_selection_single.xml │ │ ├── manage_text_line.xml │ │ ├── manage_text_link.xml │ │ ├── manage_text_rich.xml │ │ ├── manage_unknown.xml │ │ ├── manage_usergroup.xml │ │ ├── nav_header_main.xml │ │ ├── tableview_cell.xml │ │ ├── tableview_cell_check.xml │ │ ├── tableview_cell_progress.xml │ │ ├── tableview_cell_rich.xml │ │ ├── tableview_cell_stars.xml │ │ ├── tableview_column_header.xml │ │ ├── tableview_corner.xml │ │ └── tableview_row_header.xml │ │ ├── menu │ │ ├── context_menu_cell.xml │ │ ├── context_menu_column.xml │ │ ├── context_menu_table.xml │ │ ├── menu_edit_column.xml │ │ ├── menu_edit_row.xml │ │ ├── menu_edit_table.xml │ │ ├── menu_main_navigation.xml │ │ └── menu_main_toolbar.xml │ │ ├── mipmap │ │ └── ic_launcher.xml │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-ast │ │ └── strings.xml │ │ ├── values-b+en+001 │ │ └── strings.xml │ │ ├── values-bg-rBG │ │ └── strings.xml │ │ ├── values-ca │ │ └── strings.xml │ │ ├── values-cs-rCZ │ │ └── strings.xml │ │ ├── values-da │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-el │ │ └── strings.xml │ │ ├── values-es-rEC │ │ └── strings.xml │ │ ├── values-es-rMX │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-et-rEE │ │ └── strings.xml │ │ ├── values-eu │ │ └── strings.xml │ │ ├── values-fa │ │ └── strings.xml │ │ ├── values-fi-rFI │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-ga │ │ └── strings.xml │ │ ├── values-gl │ │ └── strings.xml │ │ ├── values-hr │ │ └── strings.xml │ │ ├── values-hu-rHU │ │ └── strings.xml │ │ ├── values-id │ │ └── strings.xml │ │ ├── values-is │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-ja-rJP │ │ └── strings.xml │ │ ├── values-ko │ │ └── strings.xml │ │ ├── values-lt-rLT │ │ └── strings.xml │ │ ├── values-nb-rNO │ │ └── strings.xml │ │ ├── values-night │ │ ├── booleans.xml │ │ └── colors.xml │ │ ├── values-nl │ │ └── strings.xml │ │ ├── values-pl │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-ro │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-sc │ │ └── strings.xml │ │ ├── values-sk-rSK │ │ └── strings.xml │ │ ├── values-sl │ │ └── strings.xml │ │ ├── values-sr │ │ └── strings.xml │ │ ├── values-sv │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-ug │ │ └── strings.xml │ │ ├── values-uk │ │ └── strings.xml │ │ ├── values-uz │ │ └── strings.xml │ │ ├── values-v27 │ │ └── themes.xml │ │ ├── values-vi │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rHK │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── booleans.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── preferences.xml │ └── test │ └── resources │ └── robolectric.properties ├── build.gradle ├── database ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro ├── schemas │ └── it.niedermann.nextcloud.tables.database.TablesDatabase │ │ ├── 1.json │ │ ├── 2.json │ │ ├── 3.json │ │ ├── 4.json │ │ ├── 5.json │ │ └── 6.json └── src │ ├── androidTest │ └── java │ │ └── it │ │ └── niedermann │ │ └── nextcloud │ │ └── tables │ │ └── database │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── it │ │ │ └── niedermann │ │ │ └── nextcloud │ │ │ └── tables │ │ │ └── database │ │ │ ├── DBStatus.java │ │ │ ├── TablesDatabase.java │ │ │ ├── converter │ │ │ ├── DBStatusConverter.java │ │ │ ├── EDataTypeConverter.java │ │ │ ├── InstantConverter.java │ │ │ ├── JsonElementConverter.java │ │ │ ├── LocalDateConverter.java │ │ │ ├── LocalTimeConverter.java │ │ │ ├── UriConverter.java │ │ │ ├── UserGroupTypeConverter.java │ │ │ └── VersionConverter.java │ │ │ ├── dao │ │ │ ├── AccountDao.java │ │ │ ├── ColumnDao.java │ │ │ ├── DataDao.java │ │ │ ├── DataSelectionOptionCrossRefDao.java │ │ │ ├── DataUserGroupCrossRefDao.java │ │ │ ├── DefaultValueSelectionOptionCrossRefDao.java │ │ │ ├── GenericDao.java │ │ │ ├── LinkValueDao.java │ │ │ ├── RowDao.java │ │ │ ├── SearchProviderDao.java │ │ │ ├── SelectionOptionDao.java │ │ │ ├── TableDao.java │ │ │ └── UserGroupDao.java │ │ │ ├── entity │ │ │ ├── AbstractAccountRelatedEntity.java │ │ │ ├── AbstractEntity.java │ │ │ ├── AbstractRemoteEntity.java │ │ │ ├── AbstractTableRelatedEntity.java │ │ │ ├── Account.java │ │ │ ├── Column.java │ │ │ ├── Data.java │ │ │ ├── DataSelectionOptionCrossRef.java │ │ │ ├── DataUserGroupCrossRef.java │ │ │ ├── DefaultValueSelectionOptionCrossRef.java │ │ │ ├── DefaultValueUserGroupCrossRef.java │ │ │ ├── LinkValue.java │ │ │ ├── OnSharePermission.java │ │ │ ├── Row.java │ │ │ ├── SearchProvider.java │ │ │ ├── SelectionOption.java │ │ │ ├── SynchronizationContext.java │ │ │ ├── Table.java │ │ │ ├── TextAllowedPattern.java │ │ │ ├── UserGroup.java │ │ │ └── attributes │ │ │ │ ├── DateTimeAttributes.java │ │ │ │ ├── NumberAttributes.java │ │ │ │ ├── SelectionAttributes.java │ │ │ │ ├── TextAttributes.java │ │ │ │ └── UserGroupAttributes.java │ │ │ ├── migration │ │ │ ├── Migration_1_2.java │ │ │ ├── Migration_2_3.java │ │ │ ├── Migration_3_4.java │ │ │ ├── Migration_4_5.java │ │ │ └── Migration_5_6.java │ │ │ └── model │ │ │ ├── DataTypeServiceRegistry.java │ │ │ ├── EDataType.java │ │ │ ├── EUserGroupType.java │ │ │ ├── FullColumn.java │ │ │ ├── FullData.java │ │ │ ├── FullRow.java │ │ │ ├── FullTable.java │ │ │ ├── LinkValueWithProviderId.java │ │ │ ├── NextcloudVersion.java │ │ │ ├── TablesVersion.java │ │ │ ├── Value.java │ │ │ └── Version.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── it │ └── niedermann │ └── nextcloud │ └── tables │ └── database │ └── ExampleUnitTest.java ├── fastlane └── metadata │ └── android │ └── en-US │ ├── changelogs │ ├── 1000000.txt │ ├── 1000001.txt │ ├── 1000002.txt │ ├── 1000003.txt │ ├── 1000004.txt │ ├── 1000005.txt │ ├── 1000006.txt │ ├── 1000007.txt │ ├── 1000008.txt │ ├── 1001000.txt │ ├── 1001001.txt │ ├── 1001002.txt │ ├── 1001003.txt │ ├── 2000000.txt │ ├── 2000001.txt │ ├── 2000002.txt │ ├── 2000003.txt │ ├── 2000004.txt │ ├── 2000005.txt │ ├── 2000006.txt │ ├── 2000007.txt │ ├── 2000008.txt │ ├── 2001000.txt │ ├── 2001001.txt │ ├── 2002000.txt │ ├── 2002001.txt │ ├── 2002002.txt │ ├── 2002003.txt │ ├── 2002004.txt │ └── 2002005.txt │ ├── full_description.txt │ ├── images │ ├── featureGraphic.jpg │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ └── 8.png │ ├── short_description.txt │ └── title.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── remote ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── it │ │ └── niedermann │ │ └── nextcloud │ │ └── tables │ │ └── remote │ │ ├── ApiProvider.java │ │ ├── RequestHelper.java │ │ ├── ocs │ │ ├── OcsAPI.java │ │ ├── OcsApiProvider.java │ │ ├── adapter │ │ │ └── OcsAutocompleteSourceListAdapter.java │ │ └── model │ │ │ ├── CapabilitiesResponse.java │ │ │ ├── OcsAutocompleteResult.java │ │ │ ├── OcsSearchProvider.java │ │ │ ├── OcsSearchResult.java │ │ │ └── OcsSearchResultEntry.java │ │ ├── shared │ │ ├── model │ │ │ ├── DataResponseDto.java │ │ │ └── RemoteDto.java │ │ └── util │ │ │ └── JsonArrayCollector.java │ │ ├── tablesV1 │ │ ├── TablesV1API.java │ │ ├── TablesV1ApiProvider.java │ │ ├── adapter │ │ │ ├── BooleanV1Adapter.java │ │ │ ├── EUserGroupTypeV1Adapter.java │ │ │ ├── InstantV1Adapter.java │ │ │ └── UserGroupV1ListAdapter.java │ │ └── model │ │ │ ├── ColumnRequestV1Dto.java │ │ │ ├── EUserGroupTypeV1Dto.java │ │ │ ├── FetchRowResponseV1Dto.java │ │ │ ├── UpdateColumnResponseV1Dto.java │ │ │ ├── UpdateRowRequestV1Dto.java │ │ │ └── UpdateRowResponseV1Dto.java │ │ └── tablesV2 │ │ ├── TablesV2API.java │ │ ├── TablesV2ApiProvider.java │ │ ├── adapter │ │ ├── ENodeTypeV2Adapter.java │ │ ├── EUserGroupV2Adapter.java │ │ └── InstantV2Adapter.java │ │ ├── creators │ │ ├── ColumnCreator.java │ │ ├── DataTypeCreatorServiceRegistry.java │ │ ├── NoOpCreator.java │ │ └── type │ │ │ ├── DateTimeCreator.java │ │ │ ├── NumberCreator.java │ │ │ ├── SelectionCheckCreator.java │ │ │ ├── SelectionCreator.java │ │ │ ├── TextCreator.java │ │ │ └── UserGroupCreator.java │ │ └── model │ │ ├── ColumnV2Dto.java │ │ ├── CreateColumnResponseV2Dto.java │ │ ├── CreateRowResponseV2Dto.java │ │ ├── CreateRowV2Dto.java │ │ ├── ENodeCollectionV2Dto.java │ │ ├── ENodeTypeV2Dto.java │ │ ├── EPermissionV2Dto.java │ │ ├── EUserGroupTypeV2Dto.java │ │ ├── OnSharePermissionV2Dto.java │ │ ├── SelectionOptionV2Dto.java │ │ ├── TableV2Dto.java │ │ ├── UserGroupV2Dto.java │ │ └── columns │ │ ├── CreateColumnV2Dto.java │ │ ├── CreateDateTimeColumnV2Dto.java │ │ ├── CreateNumberColumnV2Dto.java │ │ ├── CreateSelectionCheckColumnV2Dto.java │ │ ├── CreateSelectionColumnV2Dto.java │ │ ├── CreateTextColumnV2Dto.java │ │ └── CreateUserGroupColumnV2Dto.java │ └── res │ └── values │ └── strings.xml ├── repository ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── it │ │ │ └── niedermann │ │ │ └── nextcloud │ │ │ └── tables │ │ │ └── repository │ │ │ ├── AbstractRepository.java │ │ │ ├── AccountRepository.java │ │ │ ├── PreferencesRepository.java │ │ │ ├── SearchRepository.java │ │ │ ├── ServerErrorHandler.java │ │ │ ├── SyncWorker.java │ │ │ ├── TablesRepository.java │ │ │ ├── defaults │ │ │ ├── DataTypeDefaultServiceRegistry.java │ │ │ ├── DefaultValueSupplier.java │ │ │ └── supplier │ │ │ │ ├── NoOpDefaultSupplier.java │ │ │ │ ├── datetime │ │ │ │ ├── DateDefaultSupplier.java │ │ │ │ ├── DateTimeDefaultSupplier.java │ │ │ │ └── TimeDefaultSupplier.java │ │ │ │ ├── number │ │ │ │ ├── NumberDefaultSupplier.java │ │ │ │ └── NumberStarsDefaultSupplier.java │ │ │ │ ├── selection │ │ │ │ ├── SelectionCheckDefaultSupplier.java │ │ │ │ ├── SelectionDefaultSupplier.java │ │ │ │ └── SelectionMultiDefaultSupplier.java │ │ │ │ ├── text │ │ │ │ └── TextDefaultSupplier.java │ │ │ │ └── usergroup │ │ │ │ └── UserGroupDefaultSupplier.java │ │ │ ├── exception │ │ │ ├── AccountAlreadyImportedException.java │ │ │ ├── AccountNotCreatedException.java │ │ │ ├── InsufficientPermissionException.java │ │ │ └── ServerNotAvailableException.java │ │ │ ├── sync │ │ │ ├── SyncScheduler.java │ │ │ ├── exception │ │ │ │ └── SyncExceptionWithContext.java │ │ │ ├── mapper │ │ │ │ ├── RemoteMapper.java │ │ │ │ ├── ocs │ │ │ │ │ ├── OcsSearchProviderMapper.java │ │ │ │ │ └── OcsVersionMapper.java │ │ │ │ ├── shared │ │ │ │ │ └── type │ │ │ │ │ │ ├── DataV1Mapper.java │ │ │ │ │ │ ├── datetime │ │ │ │ │ │ ├── DateRemoteMapper.java │ │ │ │ │ │ ├── DateTimeRemoteMapper.java │ │ │ │ │ │ └── TimeRemoteMapper.java │ │ │ │ │ │ ├── number │ │ │ │ │ │ ├── NumberProgressRemoteMapper.java │ │ │ │ │ │ ├── NumberRemoteMapper.java │ │ │ │ │ │ └── NumberStarsRemoteMapper.java │ │ │ │ │ │ ├── selection │ │ │ │ │ │ ├── SelectionCheckDataV1Mapper.java │ │ │ │ │ │ ├── SelectionMultiDataV1Mapper.java │ │ │ │ │ │ └── SelectionSingleDataV1Mapper.java │ │ │ │ │ │ ├── text │ │ │ │ │ │ ├── TextLinkRemoteMapper.java │ │ │ │ │ │ └── TextRemoteMapper.java │ │ │ │ │ │ ├── unknown │ │ │ │ │ │ └── UnknownRemoteMapper.java │ │ │ │ │ │ └── usergroup │ │ │ │ │ │ └── UserGroupDataV1Mapper.java │ │ │ │ ├── tablesV1 │ │ │ │ │ ├── ColumnRequestV1Mapper.java │ │ │ │ │ ├── FetchAndPutRowV1Mapper.java │ │ │ │ │ └── TypeRemoteV1MapperServiceRegistry.java │ │ │ │ └── tablesV2 │ │ │ │ │ ├── ColumnV2Mapper.java │ │ │ │ │ ├── CreateRowResponseV2Mapper.java │ │ │ │ │ ├── EUserGroupTypeV2Mapper.java │ │ │ │ │ ├── OnSharePermissionV2Mapper.java │ │ │ │ │ ├── SelectionDefaultV2Mapper.java │ │ │ │ │ ├── SelectionOptionV2Mapper.java │ │ │ │ │ ├── TableV2Mapper.java │ │ │ │ │ └── UserGroupV2Mapper.java │ │ │ ├── report │ │ │ │ ├── LiveDataReporter.java │ │ │ │ ├── SyncStatus.java │ │ │ │ └── SyncStatusReporter.java │ │ │ └── treesync │ │ │ │ ├── AbstractPullOnlySyncAdapter.java │ │ │ │ ├── AbstractSyncAdapter.java │ │ │ │ ├── AccountSyncAdapter.java │ │ │ │ ├── CapabilitiesSyncAdapter.java │ │ │ │ ├── ColumnSyncAdapter.java │ │ │ │ ├── RowSyncAdapter.java │ │ │ │ ├── SearchProviderSyncAdapter.java │ │ │ │ ├── SyncAdapter.java │ │ │ │ ├── TableSyncAdapter.java │ │ │ │ ├── TreeSyncExceptionWithContext.java │ │ │ │ ├── TreeSyncScheduler.java │ │ │ │ └── UserSyncAdapter.java │ │ │ └── util │ │ │ └── ColumnReorderUtil.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── it │ └── niedermann │ └── nextcloud │ └── tables │ └── repository │ └── sync │ ├── ColumnReorderUtilTest.java │ └── mapper │ ├── ocs │ ├── OcsSearchProviderMapperTest.java │ └── OcsVersionMapperTest.java │ └── tablesV2 │ ├── EUserGroupTypeV2MapperTest.java │ ├── OnSharePermissionV2MapperTest.java │ ├── SelectionOptionV2MapperTest.java │ ├── TableV2MapperTest.java │ └── UserGroupV2MapperTest.java ├── settings.gradle ├── shared ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── it │ └── niedermann │ └── nextcloud │ └── tables │ └── shared │ ├── Constants.java │ ├── FeatureToggle.java │ └── SharedExecutors.java └── ui ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src ├── androidTest └── java │ └── it │ └── niedermann │ └── nextcloud │ └── tables │ └── ui │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml ├── java │ └── it │ │ └── niedermann │ │ └── nextcloud │ │ └── tables │ │ └── ui │ │ ├── LifecycleAwareFrameLayout.java │ │ ├── emojipicker │ │ └── EmojiPickerBottomSheet.java │ │ ├── stars │ │ └── Stars.java │ │ └── twolevelselect │ │ └── TwoLevelSelect.java └── res │ ├── drawable │ ├── baseline_clear_24.xml │ ├── baseline_restart_alt_24.xml │ ├── baseline_star_24.xml │ ├── baseline_star_border_24.xml │ └── selectable_star.xml │ ├── layout │ ├── view_emojipicker.xml │ ├── view_stars.xml │ ├── view_stars_single.xml │ └── view_two_level_select.xml │ └── values │ ├── attrs.xml │ ├── dimens.xml │ └── strings.xml └── test └── java └── it └── niedermann └── nextcloud └── tables └── ui └── ExampleUnitTest.java /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | liberapay: stefan-niedermann 4 | custom: https://www.paypal.com/donate/?hosted_button_id=8ERCNEZ6CQ666 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💼 Volume licenses 4 | url: https://www.niedermann.it 5 | about: If you are a company, a club, a university or another organization, please contact us directly for volume licenses. -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: github-actions 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 10 13 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL security scan 2 | 3 | on: 4 | pull_request: 5 | schedule: 6 | - cron: '0 12 * * *' 7 | 8 | permissions: 9 | contents: read 10 | security-events: write 11 | pull-requests: read 12 | 13 | jobs: 14 | codeql: 15 | name: CodeQL security scan 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - uses: actions/setup-java@v4 21 | with: 22 | distribution: 'temurin' 23 | java-version: '17' 24 | check-latest: true 25 | cache: 'gradle' 26 | - name: Initialize CodeQL 27 | uses: github/codeql-action/init@v3 28 | with: 29 | languages: java 30 | - name: Build debug APK 31 | run: bash ./gradlew assembleDev --stacktrace 32 | - name: Perform CodeQL Analysis 33 | uses: github/codeql-action/analyze@v3 34 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Nextcloud Tables -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /play 3 | /fdroid -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.google.gson.** { *; } 2 | 3 | # Add project specific ProGuard rules here. 4 | # You can control the set of applied configuration files using the 5 | # proguardFiles setting in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # If your project uses WebView with JS, uncomment the following 11 | # and specify the fully qualified class name to the JavaScript interface 12 | # class: 13 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 14 | # public *; 15 | #} 16 | 17 | # Uncomment this to preserve the line number information for 18 | # debugging stack traces. 19 | #-keepattributes SourceFile,LineNumberTable 20 | 21 | # If you keep the line number information, uncomment this to 22 | # hide the original source file name. 23 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/accountswitcher/AccountViewModel.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.accountswitcher; 2 | 3 | import android.app.Application; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.lifecycle.AndroidViewModel; 7 | import androidx.lifecycle.LiveData; 8 | import androidx.lifecycle.Transformations; 9 | 10 | import java.util.List; 11 | 12 | import it.niedermann.nextcloud.tables.database.entity.Account; 13 | import it.niedermann.nextcloud.tables.repository.AccountRepository; 14 | 15 | public class AccountViewModel extends AndroidViewModel { 16 | 17 | private final AccountRepository accountRepository; 18 | 19 | public AccountViewModel(@NonNull Application application) { 20 | super(application); 21 | this.accountRepository = new AccountRepository(application); 22 | } 23 | 24 | public void setCurrentAccount(@NonNull Account account) { 25 | accountRepository.setCurrentAccount(account); 26 | } 27 | 28 | public LiveData getCurrentAccount() { 29 | return accountRepository.getCurrentAccount(); 30 | } 31 | 32 | public LiveData> getAccounts() { 33 | return Transformations.switchMap(getCurrentAccount(), account -> accountRepository.getAccountsExcept$(account.getId())); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/column/edit/SearchProviderSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.column.edit; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.lifecycle.LiveData; 5 | 6 | import java.util.List; 7 | 8 | import it.niedermann.nextcloud.tables.database.entity.SearchProvider; 9 | 10 | @FunctionalInterface 11 | public interface SearchProviderSupplier { 12 | 13 | @NonNull 14 | LiveData> getSearchProvider(long accountId); 15 | } -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/column/edit/types/unknown/UnknownManager.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.column.edit.types.unknown; 2 | 3 | 4 | import android.content.Context; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.annotation.Nullable; 10 | import androidx.fragment.app.FragmentManager; 11 | 12 | import it.niedermann.nextcloud.tables.databinding.ManageUnknownBinding; 13 | import it.niedermann.nextcloud.tables.features.column.edit.types.ColumnEditView; 14 | 15 | public class UnknownManager extends ColumnEditView { 16 | 17 | public UnknownManager(@NonNull Context context) { 18 | super(context); 19 | } 20 | 21 | public UnknownManager(@NonNull Context context, @NonNull AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public UnknownManager(@NonNull Context context, 26 | @Nullable FragmentManager fragmentManager) { 27 | super(context, ManageUnknownBinding.inflate(LayoutInflater.from(context)), fragmentManager); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/column/manage/ManageColumnsViewHolder.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.column.manage; 2 | 3 | import static java.util.function.Predicate.not; 4 | 5 | import android.text.TextUtils; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import java.util.Optional; 11 | import java.util.function.Consumer; 12 | 13 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 14 | import it.niedermann.nextcloud.tables.databinding.ItemColumnBinding; 15 | 16 | public class ManageColumnsViewHolder extends RecyclerView.ViewHolder { 17 | 18 | private final ItemColumnBinding binding; 19 | 20 | public ManageColumnsViewHolder(@NonNull ItemColumnBinding binding) { 21 | super(binding.getRoot()); 22 | this.binding = binding; 23 | } 24 | 25 | public void bind(@NonNull FullColumn fullColumn, @NonNull Consumer onEdit) { 26 | final var column = fullColumn.getColumn(); 27 | final var description = Optional.ofNullable(column.getDescription()) 28 | .filter(not(TextUtils::isEmpty)) 29 | .orElse(column.getDataType().toHumanReadableString(binding.getRoot().getContext())); 30 | 31 | binding.title.setText(column.getTitle()); 32 | binding.description.setText(description); 33 | binding.edit.setOnClickListener(v -> onEdit.accept(fullColumn)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/exception/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.exception; 2 | 3 | import android.app.Activity; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | public class ExceptionHandler implements Thread.UncaughtExceptionHandler { 11 | 12 | private final Logger logger = Logger.getLogger(ExceptionHandler.class.getSimpleName()); 13 | 14 | @NonNull 15 | private final Activity activity; 16 | 17 | public ExceptionHandler(@NonNull Activity activity) { 18 | this.activity = activity; 19 | } 20 | 21 | @Override 22 | public void uncaughtException(@NonNull Thread t, @NonNull Throwable throwable) { 23 | try { 24 | logger.log(Level.SEVERE, throwable.toString(), throwable); 25 | } catch (NullPointerException ignored) { 26 | } 27 | 28 | activity.getApplicationContext().startActivity(ExceptionActivity.createIntent(activity, throwable)); 29 | activity.finish(); 30 | 31 | // This prevents the new activity from being created since at least API 35, therefore commenting it 32 | // Runtime.getRuntime().exit(0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/exception/tips/TipsModel.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.exception.tips; 2 | 3 | import android.content.Intent; 4 | 5 | import androidx.annotation.Nullable; 6 | import androidx.annotation.StringRes; 7 | 8 | @SuppressWarnings("WeakerAccess") 9 | public class TipsModel { 10 | @StringRes 11 | private final int text; 12 | @Nullable 13 | private final Intent actionIntent; 14 | 15 | TipsModel(@StringRes int text, @Nullable Intent actionIntent) { 16 | this.text = text; 17 | this.actionIntent = actionIntent; 18 | } 19 | 20 | @StringRes 21 | public int getText() { 22 | return this.text; 23 | } 24 | 25 | @Nullable 26 | public Intent getActionIntent() { 27 | return this.actionIntent; 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/row/editor/OnTextChangedListener.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.row.editor; 2 | 3 | import android.text.Editable; 4 | import android.text.TextWatcher; 5 | 6 | public interface OnTextChangedListener extends TextWatcher { 7 | 8 | @Override 9 | default void afterTextChanged(Editable s) { 10 | } 11 | 12 | @Override 13 | default void beforeTextChanged(CharSequence s, int start, int count, int after) { 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/row/editor/ProposalProvider.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.row.editor; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.lifecycle.LiveData; 5 | 6 | import java.util.Collection; 7 | 8 | import it.niedermann.nextcloud.tables.database.entity.Account; 9 | import it.niedermann.nextcloud.tables.database.entity.Column; 10 | 11 | @FunctionalInterface 12 | public interface ProposalProvider { 13 | 14 | @NonNull 15 | LiveData> getProposals(@NonNull Account account, 16 | @NonNull Column column, 17 | @NonNull String term); 18 | } -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/row/editor/type/text/TextLineEditor.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.row.editor.type.text; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import it.niedermann.nextcloud.tables.database.entity.Column; 10 | 11 | public class TextLineEditor extends TextEditor { 12 | 13 | public TextLineEditor(@NonNull Context context) { 14 | super(context); 15 | } 16 | 17 | public TextLineEditor(@NonNull Context context, @Nullable AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | public TextLineEditor(@NonNull Context context, 22 | @NonNull Column column) { 23 | super(context, column); 24 | 25 | binding.editText.setMaxLines(1); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/table/edit/EditTableViewModel.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.table.edit; 2 | 3 | import android.app.Application; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.lifecycle.AndroidViewModel; 7 | 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | import it.niedermann.nextcloud.tables.database.entity.Account; 11 | import it.niedermann.nextcloud.tables.database.entity.Table; 12 | import it.niedermann.nextcloud.tables.repository.TablesRepository; 13 | 14 | public class EditTableViewModel extends AndroidViewModel { 15 | 16 | private final TablesRepository tablesRepository; 17 | 18 | public EditTableViewModel(@NonNull Application application) { 19 | super(application); 20 | tablesRepository = new TablesRepository(application); 21 | } 22 | 23 | public CompletableFuture createTable(@NonNull Account account, 24 | @NonNull Table table) { 25 | return tablesRepository.createTable(account, table); 26 | } 27 | 28 | public CompletableFuture updateTable(@NonNull Account account, 29 | @NonNull Table table) { 30 | return tablesRepository.updateTable(account, table); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/table/view/viewholder/RowHeaderViewHolder.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.table.view.viewholder; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.evrencoskun.tableview.adapter.recyclerview.holder.AbstractViewHolder; 8 | 9 | import it.niedermann.nextcloud.tables.database.DBStatus; 10 | import it.niedermann.nextcloud.tables.database.entity.Row; 11 | import it.niedermann.nextcloud.tables.databinding.TableviewRowHeaderBinding; 12 | 13 | public class RowHeaderViewHolder extends AbstractViewHolder { 14 | public final TableviewRowHeaderBinding binding; 15 | 16 | public RowHeaderViewHolder(@NonNull TableviewRowHeaderBinding binding) { 17 | super(binding.getRoot()); 18 | this.binding = binding; 19 | } 20 | 21 | public void bind(@NonNull Row row) { 22 | this.binding.sync.setVisibility(row.getStatus() == DBStatus.VOID ? View.INVISIBLE : View.VISIBLE); 23 | itemView.requestLayout(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/table/view/viewholder/ViewHolderFactory.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.table.view.viewholder; 2 | 3 | import android.view.ViewGroup; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 8 | 9 | public abstract class ViewHolderFactory { 10 | 11 | @NonNull 12 | protected final DefaultValueSupplier defaultValueSupplier; 13 | 14 | protected ViewHolderFactory(@NonNull DefaultValueSupplier defaultValueSupplier) { 15 | this.defaultValueSupplier = defaultValueSupplier; 16 | } 17 | 18 | public abstract CellViewHolder create(@NonNull ViewGroup parent); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/table/view/viewholder/types/datetime/DateCellViewHolder.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.table.view.viewholder.types.datetime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.format.FormatStyle; 7 | import java.util.Optional; 8 | 9 | import it.niedermann.nextcloud.tables.database.entity.Data; 10 | import it.niedermann.nextcloud.tables.database.model.Value; 11 | import it.niedermann.nextcloud.tables.databinding.TableviewCellBinding; 12 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 13 | 14 | public class DateCellViewHolder extends AbstractDateTimeCellViewHolder { 15 | 16 | public DateCellViewHolder(@NonNull TableviewCellBinding binding, 17 | @NonNull DefaultValueSupplier defaultValueSupplier) { 18 | super(binding, defaultValueSupplier); 19 | } 20 | 21 | @Override 22 | protected String formatValue(@NonNull Data data) { 23 | return Optional.of(data.getValue()) 24 | .map(Value::getDateValue) 25 | .map(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)::format) 26 | .orElse(null); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/table/view/viewholder/types/datetime/DateTimeCellViewHolder.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.table.view.viewholder.types.datetime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.time.ZoneId; 6 | import java.time.format.DateTimeFormatter; 7 | import java.time.format.DateTimeParseException; 8 | import java.time.format.FormatStyle; 9 | import java.util.Optional; 10 | 11 | import it.niedermann.nextcloud.tables.database.entity.Data; 12 | import it.niedermann.nextcloud.tables.database.model.Value; 13 | import it.niedermann.nextcloud.tables.databinding.TableviewCellBinding; 14 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 15 | 16 | public class DateTimeCellViewHolder extends AbstractDateTimeCellViewHolder { 17 | 18 | public DateTimeCellViewHolder(@NonNull TableviewCellBinding binding, 19 | @NonNull DefaultValueSupplier defaultValueSupplier) { 20 | super(binding, defaultValueSupplier); 21 | } 22 | 23 | @Override 24 | protected String formatValue(@NonNull Data data) throws DateTimeParseException { 25 | return Optional.of(data.getValue()) 26 | .map(Value::getInstantValue) 27 | .map(instant -> instant.atZone(ZoneId.systemDefault())) 28 | .map(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)::format) 29 | .orElse(null); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/features/table/view/viewholder/types/datetime/TimeCellViewHolder.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.features.table.view.viewholder.types.datetime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.format.FormatStyle; 7 | import java.util.Optional; 8 | 9 | import it.niedermann.nextcloud.tables.database.entity.Data; 10 | import it.niedermann.nextcloud.tables.database.model.Value; 11 | import it.niedermann.nextcloud.tables.databinding.TableviewCellBinding; 12 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 13 | 14 | public class TimeCellViewHolder extends AbstractDateTimeCellViewHolder { 15 | 16 | public TimeCellViewHolder(@NonNull TableviewCellBinding binding, 17 | @NonNull DefaultValueSupplier defaultValueSupplier) { 18 | super(binding, defaultValueSupplier); 19 | } 20 | 21 | @Override 22 | protected String formatValue(@NonNull Data data) { 23 | return Optional.of(data.getValue()) 24 | .map(Value::getTimeValue) 25 | .map(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM)::format) 26 | .orElse(null); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/util/AvatarUtil.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.util; 2 | 3 | import android.net.Uri; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Px; 7 | 8 | import com.bumptech.glide.Glide; 9 | import com.bumptech.glide.load.model.GlideUrl; 10 | 11 | import it.niedermann.nextcloud.sso.glide.SingleSignOnUrl; 12 | import it.niedermann.nextcloud.tables.database.entity.Account; 13 | 14 | public class AvatarUtil { 15 | 16 | /** 17 | * @return The {@link #getAvatarUrl(Account, int, String)} of this {@link Account} 18 | */ 19 | public GlideUrl getAvatarUrl(@NonNull Account account, @Px int size) { 20 | return getAvatarUrl(account, size, account.getUserName()); 21 | } 22 | 23 | /** 24 | * @return a {@link GlideUrl} to fetch the avatar of the given userName from the instance of this {@link Account} via {@link Glide}. 25 | */ 26 | public GlideUrl getAvatarUrl(@NonNull Account account, @Px int size, @NonNull String userName) { 27 | return new SingleSignOnUrl(account.getAccountName(), account.getUrl() + "/index.php/avatar/" + Uri.encode(userName) + "/" + size); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/util/DimensionUtil.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.util; 2 | 3 | import static androidx.core.util.TypedValueCompat.spToPx; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.annotation.Px; 9 | 10 | public class DimensionUtil { 11 | 12 | private DimensionUtil() { 13 | // Util class 14 | } 15 | 16 | /// [Source](https://chrisdavies.github.io/sp-to-em/) 17 | @Px 18 | public static int emToPx(@NonNull Context context, float em) { 19 | return (int) (spToPx(em / 0.0624f, context.getResources().getDisplayMetrics())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/util/EmojiDrawable.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.ColorFilter; 6 | import android.graphics.Paint; 7 | import android.graphics.PixelFormat; 8 | import android.graphics.drawable.Drawable; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.emoji2.widget.EmojiTextView; 12 | 13 | public class EmojiDrawable extends Drawable { 14 | private final String emoji; 15 | private final Paint paint; 16 | 17 | public EmojiDrawable(@NonNull Context context, @NonNull String emoji) { 18 | this.emoji = emoji; 19 | this.paint = new Paint(new EmojiTextView(context).getPaint()); 20 | } 21 | 22 | 23 | @Override 24 | public void draw(@NonNull Canvas canvas) { 25 | canvas.drawText(emoji, 8, (float) getBounds().height() / 1.4f, paint); 26 | } 27 | 28 | @Override 29 | public void setAlpha(int alpha) { 30 | paint.setAlpha(alpha); 31 | } 32 | 33 | @Override 34 | public void setColorFilter(ColorFilter colorFilter) { 35 | paint.setColorFilter(colorFilter); 36 | } 37 | 38 | @Override 39 | public int getOpacity() { 40 | return switch (paint.getAlpha()) { 41 | case 0 -> PixelFormat.TRANSPARENT; 42 | case 255 -> PixelFormat.OPAQUE; 43 | default -> PixelFormat.TRANSLUCENT; 44 | }; 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/util/TextLinkUtil.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.util; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import java.util.Optional; 10 | 11 | import it.niedermann.nextcloud.tables.R; 12 | import it.niedermann.nextcloud.tables.database.entity.LinkValue; 13 | 14 | public class TextLinkUtil { 15 | 16 | /// @noinspection DataFlowIssue 17 | @Nullable 18 | public static String getLinkAsDisplayValue(@NonNull Context context, @NonNull LinkValue linkValue) { 19 | final var value = Optional.of(linkValue) 20 | .map(LinkValue::getValue) 21 | .map(Uri::toString) 22 | .orElse(null); 23 | 24 | final var title = Optional.of(linkValue) 25 | .map(LinkValue::getTitle); 26 | 27 | return title 28 | .map(s -> context.getString(R.string.format_text_link, s, value)) 29 | .orElse(value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/it/niedermann/nextcloud/tables/util/UserGroupUtil.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.util; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import java.util.Optional; 10 | 11 | import it.niedermann.nextcloud.tables.R; 12 | import it.niedermann.nextcloud.tables.database.entity.LinkValue; 13 | 14 | public class UserGroupUtil { 15 | 16 | /// @noinspection DataFlowIssue 17 | @Nullable 18 | public static String getLinkAsDisplayValue(@NonNull Context context, @NonNull LinkValue linkValue) { 19 | final var value = Optional.of(linkValue) 20 | .map(LinkValue::getValue) 21 | .map(Uri::toString) 22 | .orElse(null); 23 | 24 | final var title = Optional.of(linkValue) 25 | .map(LinkValue::getTitle); 26 | 27 | return title 28 | .map(s -> context.getString(R.string.format_text_link, s, value)) 29 | .orElse(value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_access_time_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_calendar_today_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_edit_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_link_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_numbers_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_playlist_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_post_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_question_mark_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_short_text_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_warning_amber_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_alpha_check_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_account_circle_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_brightness_2_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_check_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_delete_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_network_wifi_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_person_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_person_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_sync_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_info_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_lightbulb_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selected_check.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_circular.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 17 | 18 | 19 | 24 | 25 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_datetime.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_number_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_number_stars.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_rich.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_selection.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_selection_check.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_selection_multi.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_textview.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_table.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_account_and_version.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_option.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_date.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_datetime.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_number_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_number_stars.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_selection_check.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_selection_multi.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_text_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_text_rich.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manage_unknown.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_cell.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_cell_check.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_cell_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_cell_rich.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_cell_stars.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_column_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tableview_row_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/menu/context_menu_cell.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 15 | 20 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/menu/context_menu_column.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/menu/context_menu_table.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 15 | 20 | 25 | 30 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_edit_column.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_edit_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_edit_table.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 17 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/booleans.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-v27/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/booleans.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 8dp 7 | 176dp 8 | 9 | 100dp 10 | 42dp 11 | 12 | 16dp 13 | 40dp 14 | 15 | 40dp 16 | 24dp 17 | 16sp 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 18 | 19 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/test/resources/robolectric.properties: -------------------------------------------------------------------------------- 1 | sdk=26, 33 -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.version_desugar = '2.1.5' 5 | ext.version_nextcloud_common = '0.25.0' 6 | ext.version_nextcloud_commons = '2.3.7' 7 | ext.version_android_commons = '1.1.0' 8 | ext.version_sso = '1.3.2' 9 | ext.version_glide = '4.16.0' 10 | ext.version_emoji = '1.5.0' 11 | ext.version_retrofit = '2.11.0' 12 | ext.version_mapstruct = '1.6.3' 13 | } 14 | 15 | plugins { 16 | id 'com.android.application' version '8.10.1' apply false 17 | id 'com.android.library' version '8.10.1' apply false 18 | } 19 | 20 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /database/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/database/consumer-rules.pro -------------------------------------------------------------------------------- /database/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.google.gson.** { *; } 2 | 3 | # Add project specific ProGuard rules here. 4 | # You can control the set of applied configuration files using the 5 | # proguardFiles setting in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # If your project uses WebView with JS, uncomment the following 11 | # and specify the fully qualified class name to the JavaScript interface 12 | # class: 13 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 14 | # public *; 15 | #} 16 | 17 | # Uncomment this to preserve the line number information for 18 | # debugging stack traces. 19 | #-keepattributes SourceFile,LineNumberTable 20 | 21 | # If you keep the line number information, uncomment this to 22 | # hide the original source file name. 23 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /database/src/androidTest/java/it/niedermann/nextcloud/tables/database/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | import androidx.test.platform.app.InstrumentationRegistry; 9 | 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("it.niedermann.nextcloud.types.database.test", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /database/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/DBStatus.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | public enum DBStatus { 6 | 7 | VOID(null), 8 | LOCAL_EDITED("LOCAL_EDITED"), 9 | LOCAL_DELETED("LOCAL_DELETED"); 10 | 11 | @Nullable 12 | public final String title; 13 | 14 | DBStatus(@Nullable String title) { 15 | this.title = title; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/DBStatusConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.TypeConverter; 5 | 6 | import java.util.Objects; 7 | 8 | import it.niedermann.nextcloud.tables.database.DBStatus; 9 | 10 | public class DBStatusConverter { 11 | 12 | @TypeConverter 13 | public static DBStatus dbStatusFromString(@Nullable String value) { 14 | for (final var status : DBStatus.values()) { 15 | if (Objects.equals(status.title, value)) { 16 | return status; 17 | } 18 | } 19 | 20 | return DBStatus.VOID; 21 | } 22 | 23 | @TypeConverter 24 | public static String dbStatusToString(@Nullable DBStatus status) { 25 | return status == null ? null : status.title; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/EDataTypeConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.TypeConverter; 5 | 6 | import it.niedermann.nextcloud.tables.database.model.EDataType; 7 | import it.niedermann.nextcloud.tables.shared.FeatureToggle; 8 | 9 | public class EDataTypeConverter { 10 | 11 | @TypeConverter 12 | public static EDataType databaseTypeFromString(@Nullable Integer dataType) { 13 | if (dataType == null) { 14 | return EDataType.UNKNOWN; 15 | } 16 | 17 | try { 18 | return EDataType.findById(dataType); 19 | 20 | } catch (NumberFormatException e) { 21 | if (FeatureToggle.STRICT_MODE.enabled) { 22 | throw e; 23 | } 24 | 25 | return EDataType.UNKNOWN; 26 | } 27 | } 28 | 29 | @TypeConverter 30 | public static Integer databaseTypeToString(@Nullable EDataType dataType) { 31 | if (dataType == null || EDataType.UNKNOWN.equals(dataType)) { 32 | return null; 33 | } 34 | 35 | return dataType.getId(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/InstantConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.TypeConverter; 5 | 6 | import java.time.Instant; 7 | import java.util.Optional; 8 | 9 | public class InstantConverter { 10 | 11 | @TypeConverter 12 | public static Instant longToInstant(@Nullable Long instant) { 13 | return Optional.ofNullable(instant) 14 | .map(Instant::ofEpochMilli) 15 | .orElse(null); 16 | } 17 | 18 | @TypeConverter 19 | public static Long instantToLong(@Nullable Instant instant) { 20 | return Optional.ofNullable(instant) 21 | .map(Instant::toEpochMilli) 22 | .orElse(null); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/JsonElementConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.TypeConverter; 5 | 6 | import com.google.gson.JsonElement; 7 | import com.google.gson.JsonParser; 8 | 9 | public class JsonElementConverter { 10 | 11 | @TypeConverter 12 | public static JsonElement jsonElementFromString(@Nullable String value) { 13 | if (value == null) { 14 | return null; 15 | } 16 | 17 | return JsonParser.parseString(value); 18 | } 19 | 20 | @TypeConverter 21 | public static String jsonElementToString(@Nullable JsonElement value) { 22 | if (value == null) { 23 | return null; 24 | } 25 | 26 | return value.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/LocalDateConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.TypeConverter; 5 | 6 | import java.time.LocalDate; 7 | import java.util.Optional; 8 | 9 | public class LocalDateConverter { 10 | 11 | @TypeConverter 12 | public static LocalDate longToLocalDate(@Nullable Long src) { 13 | return Optional.ofNullable(src) 14 | .map(LocalDate::ofEpochDay) 15 | .orElse(null); 16 | } 17 | 18 | @TypeConverter 19 | public static Long localDateToLong(@Nullable LocalDate src) { 20 | return Optional.ofNullable(src) 21 | .map(LocalDate::toEpochDay) 22 | .orElse(null); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/LocalTimeConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.TypeConverter; 5 | 6 | import java.time.LocalTime; 7 | import java.util.Optional; 8 | 9 | public class LocalTimeConverter { 10 | 11 | @TypeConverter 12 | public static LocalTime longToLocalTime(@Nullable Integer src) { 13 | return Optional.ofNullable(src) 14 | .map(LocalTime::ofSecondOfDay) 15 | .orElse(null); 16 | } 17 | 18 | @TypeConverter 19 | public static Integer instantToInteger(@Nullable LocalTime src) { 20 | return Optional.ofNullable(src) 21 | .map(LocalTime::toSecondOfDay) 22 | .orElse(null); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/UriConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import android.net.Uri; 4 | 5 | import androidx.annotation.Nullable; 6 | import androidx.room.TypeConverter; 7 | 8 | import java.util.Optional; 9 | 10 | public class UriConverter { 11 | 12 | @TypeConverter 13 | public static Uri dbStatusFromString(@Nullable String link) { 14 | return Uri.parse(link); 15 | } 16 | 17 | @TypeConverter 18 | public static String dbStatusToString(@Nullable Uri link) { 19 | return Optional 20 | .ofNullable(link) 21 | .map(Uri::toString) 22 | .orElse(null); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/converter/UserGroupTypeConverter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.converter; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.TypeConverter; 5 | 6 | import it.niedermann.nextcloud.tables.database.model.EUserGroupType; 7 | 8 | public class UserGroupTypeConverter { 9 | 10 | @TypeConverter 11 | public static EUserGroupType userGroupTypeFromInteger(@Nullable Integer type) { 12 | if (type == null) { 13 | return EUserGroupType.UNKNOWN; 14 | } 15 | 16 | return EUserGroupType.findByRemoteId(type); 17 | } 18 | 19 | @TypeConverter 20 | public static Integer userGroupTypeToInteger(@Nullable EUserGroupType type) { 21 | if (type == null || EUserGroupType.UNKNOWN.equals(type)) { 22 | return null; 23 | } 24 | 25 | return type.getRemoteType(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/dao/DataSelectionOptionCrossRefDao.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.dao; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.Query; 7 | import androidx.room.Upsert; 8 | 9 | import java.util.List; 10 | 11 | import it.niedermann.nextcloud.tables.database.entity.DataSelectionOptionCrossRef; 12 | 13 | @Dao 14 | public interface DataSelectionOptionCrossRefDao { 15 | 16 | @Insert 17 | long insert(DataSelectionOptionCrossRef entity); 18 | 19 | @Insert 20 | long[] insert(DataSelectionOptionCrossRef... entity); 21 | 22 | @Upsert 23 | long upsert(DataSelectionOptionCrossRef entity); 24 | 25 | @Delete 26 | void delete(DataSelectionOptionCrossRef... entity); 27 | 28 | @Query(""" 29 | SELECT crossRef.* 30 | FROM DataSelectionOptionCrossRef crossRef 31 | WHERE crossRef.dataId = :dataId 32 | """) 33 | List getCrossRefs(long dataId); 34 | } 35 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/dao/DataUserGroupCrossRefDao.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.dao; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.Query; 7 | import androidx.room.Upsert; 8 | 9 | import java.util.List; 10 | 11 | import it.niedermann.nextcloud.tables.database.entity.DataUserGroupCrossRef; 12 | 13 | @Dao 14 | public interface DataUserGroupCrossRefDao { 15 | 16 | @Insert 17 | long insert(DataUserGroupCrossRef entity); 18 | 19 | @Upsert 20 | long upsert(DataUserGroupCrossRef entity); 21 | 22 | @Insert 23 | long[] insert(DataUserGroupCrossRef... entity); 24 | 25 | @Delete 26 | void delete(DataUserGroupCrossRef... entity); 27 | 28 | @Query(""" 29 | DELETE FROM DataUserGroupCrossRef 30 | WHERE DataUserGroupCrossRef.dataId = :dataId 31 | """) 32 | void delete(long dataId); 33 | 34 | @Query(""" 35 | SELECT crossRef.* 36 | FROM DataUserGroupCrossRef crossRef 37 | WHERE crossRef.dataId = :dataId 38 | """) 39 | List getCrossRefs(long dataId); 40 | } 41 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/dao/DefaultValueSelectionOptionCrossRefDao.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.dao; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.Query; 7 | import androidx.room.Upsert; 8 | 9 | import java.util.Collection; 10 | 11 | import it.niedermann.nextcloud.tables.database.entity.DefaultValueSelectionOptionCrossRef; 12 | 13 | @Dao 14 | public interface DefaultValueSelectionOptionCrossRefDao { 15 | 16 | @Insert 17 | long insert(DefaultValueSelectionOptionCrossRef entity); 18 | 19 | @Insert 20 | long[] insert(DefaultValueSelectionOptionCrossRef... entity); 21 | 22 | @Upsert 23 | long upsert(DefaultValueSelectionOptionCrossRef entity); 24 | 25 | @Delete 26 | void deleteExcept(DefaultValueSelectionOptionCrossRef... entity); 27 | 28 | @Query(""" 29 | DELETE FROM DefaultValueSelectionOptionCrossRef 30 | WHERE columnId = :columnId 31 | AND selectionOptionId IN (:selectionOptionIds) 32 | """) 33 | void delete(long columnId, Collection selectionOptionIds); 34 | 35 | @Query(""" 36 | DELETE FROM DefaultValueSelectionOptionCrossRef 37 | WHERE columnId = :columnId 38 | AND selectionOptionId NOT IN (:selectionOptionIds) 39 | """) 40 | void deleteExcept(long columnId, Collection selectionOptionIds); 41 | } 42 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/dao/GenericDao.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.dao; 2 | 3 | import androidx.room.Delete; 4 | import androidx.room.Insert; 5 | import androidx.room.Update; 6 | import androidx.room.Upsert; 7 | 8 | public interface GenericDao { 9 | 10 | @Insert 11 | long insert(T entity); 12 | 13 | @SuppressWarnings("unchecked") 14 | @Insert 15 | long[] insert(T... entity); 16 | 17 | @SuppressWarnings("unchecked") 18 | @Update 19 | void update(T... entity); 20 | 21 | /** 22 | * @noinspection unchecked 23 | */ 24 | @Upsert 25 | void upsert(T... entity); 26 | 27 | @SuppressWarnings("unchecked") 28 | @Delete 29 | void delete(T... entity); 30 | } 31 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/dao/UserGroupDao.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.dao; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.room.Dao; 5 | import androidx.room.Query; 6 | import androidx.room.Transaction; 7 | 8 | import it.niedermann.nextcloud.tables.database.entity.UserGroup; 9 | 10 | @Dao 11 | public interface UserGroupDao extends GenericDao { 12 | 13 | @Transaction 14 | @Query(""" 15 | SELECT EXISTS( 16 | SELECT u.id 17 | FROM UserGroup u 18 | WHERE u.accountId = :accountId 19 | AND u.remoteId = :remoteId 20 | LIMIT 1 21 | ) 22 | """) 23 | boolean exists(long accountId, String remoteId); 24 | 25 | @Query(""" 26 | SELECT u.id 27 | FROM UserGroup u 28 | WHERE u.accountId = :accountId 29 | AND u.remoteId = :remoteId 30 | """) 31 | Long getIdByRemoteId(long accountId, String remoteId); 32 | 33 | @Transaction 34 | default Long upsertAndGetId(@NonNull UserGroup userGroup) { 35 | upsert(userGroup); 36 | return getIdByRemoteId(userGroup.getAccountId(), userGroup.getRemoteId()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/AbstractEntity.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.room.Entity; 5 | import androidx.room.Ignore; 6 | import androidx.room.PrimaryKey; 7 | 8 | import java.io.Serializable; 9 | import java.util.Objects; 10 | 11 | @Entity() 12 | public abstract class AbstractEntity implements Serializable { 13 | 14 | @PrimaryKey(autoGenerate = true) 15 | protected long id; 16 | 17 | 18 | public AbstractEntity() { 19 | // Default constructor 20 | } 21 | 22 | @Ignore 23 | public AbstractEntity(@NonNull AbstractEntity entity) { 24 | this.id = entity.getId(); 25 | } 26 | 27 | public long getId() { 28 | return id; 29 | } 30 | 31 | public void setId(long id) { 32 | this.id = id; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (this == o) return true; 38 | if (o == null || getClass() != o.getClass()) return false; 39 | AbstractEntity that = (AbstractEntity) o; 40 | return id == that.id; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Objects.hash(id); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/DataSelectionOptionCrossRef.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.ForeignKey; 5 | import androidx.room.Index; 6 | 7 | @Entity( 8 | primaryKeys = {"dataId", "selectionOptionId"}, 9 | foreignKeys = { 10 | @ForeignKey( 11 | entity = Data.class, 12 | parentColumns = "id", 13 | childColumns = "dataId", 14 | onDelete = ForeignKey.CASCADE 15 | ), 16 | @ForeignKey( 17 | entity = SelectionOption.class, 18 | parentColumns = "id", 19 | childColumns = "selectionOptionId", 20 | onDelete = ForeignKey.CASCADE 21 | ) 22 | }, 23 | indices = { 24 | @Index(value = {"dataId"}), 25 | @Index(value = {"selectionOptionId"}) 26 | } 27 | ) 28 | public record DataSelectionOptionCrossRef( 29 | long dataId, 30 | long selectionOptionId 31 | ) { 32 | } 33 | 34 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/DataUserGroupCrossRef.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.ForeignKey; 5 | import androidx.room.Index; 6 | 7 | 8 | @Entity( 9 | primaryKeys = {"dataId", "userGroupId"}, 10 | foreignKeys = { 11 | @ForeignKey( 12 | entity = Data.class, 13 | parentColumns = "id", 14 | childColumns = "dataId", 15 | onDelete = ForeignKey.CASCADE 16 | ), 17 | @ForeignKey( 18 | entity = UserGroup.class, 19 | parentColumns = "id", 20 | childColumns = "userGroupId", 21 | onDelete = ForeignKey.CASCADE 22 | ) 23 | }, 24 | indices = { 25 | @Index(value = {"dataId"}), 26 | @Index(value = {"userGroupId"}) 27 | } 28 | ) 29 | public record DataUserGroupCrossRef( 30 | long dataId, 31 | long userGroupId 32 | ) { 33 | } 34 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/SynchronizationContext.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import androidx.room.Ignore; 6 | 7 | import java.io.Serializable; 8 | 9 | import it.niedermann.nextcloud.tables.database.DBStatus; 10 | 11 | public record SynchronizationContext( 12 | @Nullable DBStatus status, 13 | @Nullable String eTag 14 | ) implements Serializable { 15 | 16 | @Ignore 17 | public SynchronizationContext() { 18 | this(DBStatus.VOID, null); 19 | } 20 | 21 | @Ignore 22 | public SynchronizationContext(@NonNull SynchronizationContext remoteContext) { 23 | this(remoteContext.status, remoteContext.eTag); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/TextAllowedPattern.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.room.Entity; 5 | import androidx.room.Ignore; 6 | 7 | import java.util.Objects; 8 | 9 | @Entity( 10 | inheritSuperIndices = true 11 | ) 12 | public class TextAllowedPattern extends AbstractEntity { 13 | 14 | private String pattern; 15 | 16 | public TextAllowedPattern() { 17 | // Default constructor 18 | } 19 | 20 | @Ignore 21 | public TextAllowedPattern(@NonNull TextAllowedPattern textAllowedPattern) { 22 | super(textAllowedPattern); 23 | this.pattern = textAllowedPattern.pattern; 24 | } 25 | 26 | public String getPattern() { 27 | return pattern; 28 | } 29 | 30 | public void setPattern(String pattern) { 31 | this.pattern = pattern; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object o) { 36 | if (this == o) return true; 37 | if (o == null || getClass() != o.getClass()) return false; 38 | if (!super.equals(o)) return false; 39 | TextAllowedPattern that = (TextAllowedPattern) o; 40 | return Objects.equals(pattern, that.pattern); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Objects.hash(super.hashCode(), pattern); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/attributes/DateTimeAttributes.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity.attributes; 2 | 3 | import java.io.Serializable; 4 | 5 | public record DateTimeAttributes( 6 | ) implements Serializable { 7 | } 8 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/attributes/NumberAttributes.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity.attributes; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.room.Ignore; 5 | 6 | import java.io.Serializable; 7 | 8 | public record NumberAttributes( 9 | @Nullable Double numberMin, 10 | @Nullable Double numberMax, 11 | @Nullable Integer numberDecimals, 12 | @Nullable String numberPrefix, 13 | @Nullable String numberSuffix 14 | ) implements Serializable { 15 | 16 | @Ignore 17 | public NumberAttributes() { 18 | this(null, null, null, null, null); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/attributes/SelectionAttributes.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity.attributes; 2 | 3 | import java.io.Serializable; 4 | 5 | public record SelectionAttributes( 6 | ) implements Serializable { 7 | } 8 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/attributes/TextAttributes.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity.attributes; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import androidx.room.Ignore; 6 | 7 | import java.io.Serializable; 8 | 9 | public record TextAttributes( 10 | @Nullable String textAllowedPattern, 11 | @Nullable Integer textMaxLength 12 | ) implements Serializable { 13 | 14 | @Ignore 15 | public TextAttributes() { 16 | this(null, null); 17 | } 18 | 19 | @Ignore 20 | public TextAttributes(@NonNull TextAttributes textAttributes) { 21 | this(textAttributes.textAllowedPattern, textAttributes.textMaxLength); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/entity/attributes/UserGroupAttributes.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.entity.attributes; 2 | 3 | import androidx.room.ColumnInfo; 4 | import androidx.room.Ignore; 5 | 6 | import java.io.Serializable; 7 | 8 | public record UserGroupAttributes( 9 | boolean usergroupMultipleItems, 10 | boolean usergroupSelectUsers, 11 | boolean usergroupSelectGroups, 12 | @ColumnInfo(defaultValue = "0") 13 | boolean usergroupSelectTeams, 14 | boolean showUserStatus 15 | ) implements Serializable { 16 | 17 | @Ignore 18 | public UserGroupAttributes() { 19 | this(false, false, false, false, false); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/migration/Migration_3_4.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.migration; 2 | 3 | import androidx.room.DeleteColumn; 4 | import androidx.room.migration.AutoMigrationSpec; 5 | 6 | @DeleteColumn.Entries( 7 | @DeleteColumn( 8 | tableName = "UserGroup", 9 | columnName = "key" 10 | ) 11 | ) 12 | public class Migration_3_4 implements AutoMigrationSpec { 13 | } 14 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/migration/Migration_4_5.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.migration; 2 | 3 | import androidx.room.migration.AutoMigrationSpec; 4 | 5 | /// Adds `archived` and `favorite` columns to `Table` entity 6 | public class Migration_4_5 implements AutoMigrationSpec { 7 | } 8 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/model/EUserGroupType.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.model; 2 | 3 | public enum EUserGroupType { 4 | UNKNOWN(-1), 5 | USER(0), 6 | GROUP(1), 7 | TEAM(7), 8 | ; 9 | 10 | private final int remoteType; 11 | 12 | EUserGroupType(int remoteType) { 13 | this.remoteType = remoteType; 14 | } 15 | 16 | public static EUserGroupType findByRemoteId(int remoteType) { 17 | for (final var value : values()) { 18 | if (value.remoteType == remoteType) { 19 | return value; 20 | } 21 | } 22 | 23 | return UNKNOWN; 24 | } 25 | 26 | public int getRemoteType() { 27 | return remoteType; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/model/NextcloudVersion.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | public class NextcloudVersion extends Version { 6 | 7 | private static final Version V_25_0_0 = new Version("25.0.0", 25, 0, 0); 8 | 9 | public NextcloudVersion(@NonNull String version, int major, int minor, int patch) { 10 | super(version, major, minor, patch); 11 | } 12 | 13 | public static NextcloudVersion parse(@NonNull String version) { 14 | return of(Version.parse(version)); 15 | } 16 | 17 | public static NextcloudVersion of(@NonNull Version version) { 18 | return new NextcloudVersion(version.getVersion(), version.getMajor(), version.getMinor(), version.getPatch()); 19 | } 20 | 21 | public boolean isSupported() { 22 | return isGreaterThanOrEqual(V_25_0_0); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /database/src/main/java/it/niedermann/nextcloud/tables/database/model/TablesVersion.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | public class TablesVersion extends Version { 6 | 7 | public static final TablesVersion V_0_5_0 = new TablesVersion("0.5.0", 0, 5, 0); 8 | public static final TablesVersion V_0_8_0 = new TablesVersion("0.8.0", 0, 8, 0); 9 | 10 | public TablesVersion(@NonNull String version, int major, int minor, int patch) { 11 | super(version, major, minor, patch); 12 | } 13 | 14 | public static TablesVersion parse(@NonNull String version) { 15 | return of(Version.parse(version)); 16 | } 17 | 18 | public static TablesVersion of(@NonNull Version version) { 19 | return new TablesVersion(version.getVersion(), version.getMajor(), version.getMinor(), version.getPatch()); 20 | } 21 | 22 | public boolean isSupported() { 23 | return isGreaterThanOrEqual(V_0_8_0); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Unknown 4 | Text 5 | Number 6 | Selection 7 | Date and time 8 | Users and groups 9 | 10 | Long 11 | Line 12 | Rich 13 | Link 14 | Default 15 | Stars rating 16 | Progress bar 17 | Single 18 | Multi 19 | Check 20 | Date and time 21 | Date 22 | Time 23 | -------------------------------------------------------------------------------- /database/src/test/java/it/niedermann/nextcloud/tables/database/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.database; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000000.txt: -------------------------------------------------------------------------------- 1 | Initial release ✨ 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000001.txt: -------------------------------------------------------------------------------- 1 | - feat: Enable selection editor 2 | - feat: Rich text viewer -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000002.txt: -------------------------------------------------------------------------------- 1 | - i18n: Add translations from transifex -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000003.txt: -------------------------------------------------------------------------------- 1 | - feat: Rich text editor 2 | - fix: Make row editor scrollable 3 | - fix: Preserve state when rotating device while editing row -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000004.txt: -------------------------------------------------------------------------------- 1 | - feat(ui): Directly display selection values in table view 2 | - feat(permissions): Respect permissions for shared tables 3 | - feat(sync): Synchronize account on app start 4 | - feat(preferences): Allow synchronizing on Wi-Fi only 5 | - enh(sync): Better handling of network errors 6 | - feat(ui): Add monochrome launcher icon 7 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000005.txt: -------------------------------------------------------------------------------- 1 | - enh: Show existing value if column type is not supported 2 | - fix: Prevent crash for numbers with constraints 3 | - fix: Prevent crash when opening account switcher dialog before importing any account 4 | - fix: Prevent crash when min or max is not configured for number columns 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000006.txt: -------------------------------------------------------------------------------- 1 | - fix: Prevent crash for selections in special circumstances 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000007.txt: -------------------------------------------------------------------------------- 1 | - fix: Make viewing and editing data more robust 2 | - fix: Fix wrong displayed selection text in special circumstances 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1000008.txt: -------------------------------------------------------------------------------- 1 | - fix: Star editor displayed wrong number of stars (#7) 2 | - fix: SQLiteException: too many SQL variables (#8) 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1001000.txt: -------------------------------------------------------------------------------- 1 | - feat: Edit columns (experimental feature) 2 | - feat: Reorder columns (experimental feature) 3 | - feat: Delete columns 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1001001.txt: -------------------------------------------------------------------------------- 1 | - enh: Disable mandatory property in column editor 2 | - fix: Allow entering decimals for number fields 3 | - fix: Hide complete table when no rows are present as workaround for crash 4 | - fix: Do not show deleted selection options in editor 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1001002.txt: -------------------------------------------------------------------------------- 1 | - enh: Disable mandatory property in column editor 2 | - fix: Allow entering decimals for number fields 3 | - fix: Hide complete table when no rows are present as workaround for crash 4 | - fix: Do not show deleted selection options in editor 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1001003.txt: -------------------------------------------------------------------------------- 1 | - fix: Prevents crash when table emoji is null 2 | - fix: Treat "null" as null for selection default 3 | - fix: Fix logging of Tables server app version 4 | - fix: Compatibility with "none" as default value for datetimes 5 | - fix: Prevents crash after deleting a table 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000000.txt: -------------------------------------------------------------------------------- 1 | Tables Android 2.0 is a complete rewrite with a maintainable and extensible long term architecture in mind. 2 | We believe that we roughly reached feature parity with Tables Android 1.0, so don't expect many new features in this release, but you can look forward to more frequent releases in the future. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000001.txt: -------------------------------------------------------------------------------- 1 | fix(sync): Prevent endless loading spinner -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000002.txt: -------------------------------------------------------------------------------- 1 | fix(ui): Fix missing create row button and invisible table content off screen 2 | fix(edit): Fix change detection of number editor, so it does not ask to save unchanged values everytime 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000003.txt: -------------------------------------------------------------------------------- 1 | fix(database)!: ⚠️ Enforce foreign key constraints (Not yet synchronized changes will be lost on update!) 2 | feat(usergroup): Synchronize and display usergroup values 3 | fix(ui): Order tables in sidebar by createdAt rather than title to match Tables server app (#147) 4 | fix(text-link): Fix updated links not being saved 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000004.txt: -------------------------------------------------------------------------------- 1 | feat: Enhanced performance by only querying the displayed data from the database 2 | fix(datetime): Fix editing and viewing datetime cells 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000005.txt: -------------------------------------------------------------------------------- 1 | fix: Extend visible range of rows by 10 before querying rows from db 2 | fix: Make use of bindPending method for rows that are not loaded from the db yet 3 | fix(sync): Decimal number is rounded up when updating (#153) 4 | fix(ui): Display column default as fallback if number/progress has no value instead of an indeterminate animation 5 | fix(ui): Use minimal possible value as fallback if number/progress has no value and the corresponding colum has no default value 6 | chore(ui): Introduce databinding in AboutActivity 7 | docs: Add CONTRIBUTING.md 8 | feat(logging): Enrich synchronization exceptions with contextual information -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000006.txt: -------------------------------------------------------------------------------- 1 | fix(sync): Remove debug code causing NumberFormatException -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000007.txt: -------------------------------------------------------------------------------- 1 | fix(ui): Fix showing / hiding FAB depending on create permission for the current table 2 | chore(deps): Update AGP to 8.8.1 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2000008.txt: -------------------------------------------------------------------------------- 1 | fix: Make SyncExceptionWithContext implement Serializable to avoid crash 2 | fix: Fix syntax error 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2001000.txt: -------------------------------------------------------------------------------- 1 | feat(table): Allow changing the emoji of tables 2 | feat(columns): Allow adding and editing columns (except single selections and user groups) 3 | feat(rows): Display default values as fallback for selection cells 4 | feat(number): Introduce internationalization to number input editor (e. g. use appropriate decimal separator) 5 | fix(number-progress): Show save prompt only when there are unsaved changes 6 | fix(import): Prevent undefined state after pressing back while importing an account 7 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2001001.txt: -------------------------------------------------------------------------------- 1 | fix(sharing): Fix permissions for shared tables (#179) 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2002000.txt: -------------------------------------------------------------------------------- 1 | fix: Avoid endless spinner when switching to the same table that is currently active 2 | feat(table): Add support for archived and favorite tables 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2002001.txt: -------------------------------------------------------------------------------- 1 | fix: Fix scrolling / swipe to refresh conflict 2 | fix: Fix crash on startup after update caused by database migration 3 | fix(number): Fix not editable numbers when min or decimals are not present in column settings 4 | fix(sync): Fix potential foreign key constraint violation at selection options 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2002002.txt: -------------------------------------------------------------------------------- 1 | fix(edit): Fix pristine state of date and time editors 2 | fix(edit): Fix NullPointerException when editing number columns with nullish attributes 3 | fix(sync): Fix potential foreign key constraint violation when synchronizing selection option relations 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2002003.txt: -------------------------------------------------------------------------------- 1 | fix: Crash after updating to 2.2.1 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2002004.txt: -------------------------------------------------------------------------------- 1 | fix(number): Fix locale depending separator character for editing number decimals 2 | fix(perf): Don't let synchronization plugging user interactions 3 | fix(perf): Do only establish connections to the files app for APIs that will actually be used 4 | fix(perf): Limit reporter synchronization to current reporter instance 5 | fix(import): Fix displaying progress while importing an account 6 | fix(import): Pressing back when importing another account returns to main view instead of closing the app 7 | fix(logging): Enhance logging context 8 | chore(logging): Replace Android Logging with Java Util Logging 9 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2002005.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/fastlane/metadata/android/en-US/changelogs/2002005.txt -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | An Android client for Nextcloud Tables App¹. 2 | 3 | ⚠️ Warning ⚠️ 4 | This app is currently under heavy development. It is not yet ready to view or manage data in a production environment. Data loss may be possible. Use at your own risc! 5 | For limitations see https://github.com/stefan-niedermann/nextcloud-tables/#readme 6 | 7 | 🚀 Features 8 | * Multiple accounts 👥 9 | * Works offline 🔌 10 | * Manage tables, columns and rows 📝 11 | * Dark mode 🌙 12 | * Translated in many languages 🌎 13 | 14 | 🔗 Requirements 15 | * Nextcloud¹ instance running 16 | * Nextcloud Android² app installed (version 3.2.2 or later) 17 | * Nextcloud Tables app enabled (version 0.5.0 or later) 18 | 19 | 📓 License 20 | All contributions to this repository are considered to be licensed under the [GNU GENERAL PUBLIC LICENSE 3+](/LICENSE). 21 | 22 | ¹ https://github.com/nextcloud/android 23 | ² https://nextcloud.com/ 24 | ³ https://github.com/nextcloud/tables/ 25 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/featureGraphic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/fastlane/metadata/android/en-US/images/featureGraphic.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Companion app for Nextcloud Tables 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Nextcloud Tables 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 2 | org.gradle.configuration-cache=true 3 | android.useAndroidX=true 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /remote/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /remote/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/remote/consumer-rules.pro -------------------------------------------------------------------------------- /remote/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.google.gson.** { *; } 2 | 3 | # Add project specific ProGuard rules here. 4 | # You can control the set of applied configuration files using the 5 | # proguardFiles setting in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # If your project uses WebView with JS, uncomment the following 11 | # and specify the fully qualified class name to the JavaScript interface 12 | # class: 13 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 14 | # public *; 15 | #} 16 | 17 | # Uncomment this to preserve the line number information for 18 | # debugging stack traces. 19 | #-keepattributes SourceFile,LineNumberTable 20 | 21 | # If you keep the line number information, uncomment this to 22 | # hide the original source file name. 23 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /remote/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/ocs/adapter/OcsAutocompleteSourceListAdapter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.ocs.adapter; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonNull; 5 | import com.google.gson.JsonSerializationContext; 6 | import com.google.gson.JsonSerializer; 7 | 8 | import java.lang.reflect.Type; 9 | import java.util.List; 10 | 11 | import it.niedermann.nextcloud.tables.remote.ocs.model.OcsAutocompleteResult; 12 | import it.niedermann.nextcloud.tables.remote.shared.util.JsonArrayCollector; 13 | 14 | public class OcsAutocompleteSourceListAdapter implements JsonSerializer> { 15 | 16 | @Override 17 | public JsonElement serialize(List src, Type typeOfSrc, JsonSerializationContext context) { 18 | if (src == null) { 19 | return JsonNull.INSTANCE; 20 | } 21 | 22 | return src 23 | .stream() 24 | .map(context::serialize) 25 | .collect(new JsonArrayCollector()); 26 | } 27 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/ocs/model/CapabilitiesResponse.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.ocs.model; 2 | 3 | import com.nextcloud.android.sso.model.ocs.OcsCapabilitiesResponse; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Utilizing {@link OcsCapabilitiesResponse} classes combined with own tables specific information 9 | */ 10 | public record CapabilitiesResponse( 11 | OcsCapabilitiesResponse.OcsVersion version, 12 | OcsCapabilities capabilities 13 | ) implements Serializable { 14 | public record OcsCapabilities( 15 | OcsCapabilitiesResponse.OcsCapabilities.OcsTheming theming, 16 | Tables tables 17 | ) implements Serializable { 18 | public record Tables( 19 | boolean enabled, 20 | String version 21 | ) implements Serializable { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/ocs/model/OcsAutocompleteResult.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.ocs.model; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.io.Serializable; 9 | 10 | /// [Documentation](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#auto-complete-and-user-search) 11 | public record OcsAutocompleteResult( 12 | @NonNull String id, 13 | @NonNull String label, 14 | @Nullable String icon, 15 | @NonNull OcsAutocompleteSource source, 16 | @Nullable String subline, 17 | @Nullable String shareWithDisplayNameUnique 18 | ) implements Serializable { 19 | 20 | public enum OcsAutocompleteSource { 21 | @SerializedName(value = "0", alternate = "users") 22 | USERS(0), 23 | @SerializedName(value = "1", alternate = "groups") 24 | GROUPS(1), 25 | @SerializedName(value = "7", alternate = "teams") 26 | TEAMS(7), 27 | ; 28 | 29 | public final int shareType; 30 | 31 | OcsAutocompleteSource(int shareType) { 32 | this.shareType = shareType; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/ocs/model/OcsSearchProvider.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.ocs.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.io.Serializable; 8 | 9 | /// @see Source 10 | public record OcsSearchProvider( 11 | @SerializedName("id") 12 | @Nullable String remoteId, 13 | @Nullable String appId, 14 | @Nullable String name, 15 | @Nullable String icon, 16 | int order, 17 | boolean inAppSearch 18 | ) implements Serializable { 19 | } 20 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/ocs/model/OcsSearchResult.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.ocs.model; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | public record OcsSearchResult( 10 | @Nullable String name, 11 | boolean isPaginated, 12 | @NonNull List entries, 13 | @Nullable Integer cursor 14 | ) implements Serializable { 15 | } 16 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/ocs/model/OcsSearchResultEntry.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.ocs.model; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import java.io.Serializable; 7 | import java.util.Map; 8 | 9 | /// @see Source 10 | public record OcsSearchResultEntry( 11 | /// @since 20.0.0 12 | @Nullable String thumbnailUrl, 13 | /// @since 20.0.0 14 | @Nullable String title, 15 | /// @since 20.0.0 16 | @Nullable String subline, 17 | /// @since 20.0.0 18 | @Nullable String resourceUrl, 19 | /// @since 20.0.0 20 | @Nullable String icon, 21 | /// @since 20.0.0 22 | boolean rounded, 23 | ///@since 20.0.0 24 | @NonNull Map attributes 25 | ) implements Serializable { 26 | } 27 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/shared/model/DataResponseDto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.shared.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.io.Serializable; 9 | 10 | public record DataResponseDto( 11 | @SerializedName("columnId") 12 | @Nullable Long remoteColumnId, 13 | @Nullable JsonElement value 14 | ) implements Serializable { 15 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/shared/model/RemoteDto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.shared.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | public interface RemoteDto { 6 | 7 | @Nullable 8 | Long remoteId(); 9 | } 10 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/shared/util/JsonArrayCollector.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.shared.util; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | 6 | import java.util.Collections; 7 | import java.util.Set; 8 | import java.util.function.BiConsumer; 9 | import java.util.function.BinaryOperator; 10 | import java.util.function.Function; 11 | import java.util.function.Supplier; 12 | import java.util.stream.Collector; 13 | 14 | public class JsonArrayCollector implements Collector { 15 | 16 | @Override 17 | public Supplier supplier() { 18 | return JsonArray::new; 19 | } 20 | 21 | @Override 22 | public BiConsumer accumulator() { 23 | return JsonArray::add; 24 | } 25 | 26 | @Override 27 | public BinaryOperator combiner() { 28 | return ((jsonArray, jsonArray2) -> { 29 | final var result = new JsonArray(); 30 | result.addAll(jsonArray); 31 | result.addAll(jsonArray2); 32 | return result; 33 | }); 34 | } 35 | 36 | @Override 37 | public Function finisher() { 38 | return result -> result; 39 | } 40 | 41 | @Override 42 | public Set characteristics() { 43 | return Collections.emptySet(); 44 | } 45 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/adapter/BooleanV1Adapter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.adapter; 2 | 3 | import com.google.gson.JsonDeserializationContext; 4 | import com.google.gson.JsonDeserializer; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonPrimitive; 7 | import com.google.gson.JsonSerializationContext; 8 | import com.google.gson.JsonSerializer; 9 | 10 | import java.lang.reflect.Type; 11 | 12 | public class BooleanV1Adapter implements JsonSerializer, JsonDeserializer { 13 | 14 | private static final JsonPrimitive TRUE = new JsonPrimitive(1); 15 | private static final JsonPrimitive FALSE = new JsonPrimitive(0); 16 | 17 | @Override 18 | public synchronized JsonElement serialize(Boolean src, Type type, JsonSerializationContext jsonSerializationContext) { 19 | return Boolean.TRUE.equals(src) ? TRUE : FALSE; 20 | } 21 | 22 | @Override 23 | public synchronized Boolean deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) { 24 | if (jsonElement == null) { 25 | return false; 26 | } 27 | 28 | try { 29 | return jsonElement.getAsBoolean(); 30 | } catch (UnsupportedOperationException | IllegalStateException e) { 31 | return false; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/adapter/EUserGroupTypeV1Adapter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.adapter; 2 | 3 | import com.google.gson.JsonDeserializationContext; 4 | import com.google.gson.JsonDeserializer; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonParseException; 7 | import com.google.gson.JsonPrimitive; 8 | import com.google.gson.JsonSerializationContext; 9 | import com.google.gson.JsonSerializer; 10 | 11 | import java.lang.reflect.Type; 12 | import java.util.Optional; 13 | 14 | import it.niedermann.nextcloud.tables.remote.tablesV1.model.EUserGroupTypeV1Dto; 15 | 16 | public class EUserGroupTypeV1Adapter implements JsonSerializer, JsonDeserializer { 17 | 18 | @Override 19 | public EUserGroupTypeV1Dto deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 20 | return Optional.ofNullable(json) 21 | .map(JsonElement::getAsInt) 22 | .map(EUserGroupTypeV1Dto::findByRemoteId) 23 | .orElse(null); 24 | } 25 | 26 | @Override 27 | public JsonElement serialize(EUserGroupTypeV1Dto src, Type typeOfSrc, JsonSerializationContext context) { 28 | return Optional.ofNullable(src) 29 | .map(EUserGroupTypeV1Dto::getRemoteId) 30 | .map(JsonPrimitive::new) 31 | .orElse(null); 32 | } 33 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/adapter/UserGroupV1ListAdapter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.adapter; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonNull; 5 | import com.google.gson.JsonPrimitive; 6 | import com.google.gson.JsonSerializationContext; 7 | import com.google.gson.JsonSerializer; 8 | 9 | import java.lang.reflect.Type; 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | import it.niedermann.nextcloud.tables.remote.tablesV1.model.ColumnRequestV1Dto; 14 | 15 | public class UserGroupV1ListAdapter implements JsonSerializer> { 16 | 17 | @Override 18 | public JsonElement serialize(List src, Type typeOfSrc, JsonSerializationContext context) { 19 | return Optional.ofNullable(src) 20 | .map(context::serialize) 21 | .map(JsonElement::toString) 22 | .map(JsonPrimitive::new) 23 | .map(JsonElement.class::cast) 24 | .orElse(JsonNull.INSTANCE); 25 | } 26 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/model/EUserGroupTypeV1Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.util.NoSuchElementException; 8 | 9 | public enum EUserGroupTypeV1Dto { 10 | USER(0), 11 | GROUP(1), 12 | CIRCLE(7), 13 | ; 14 | 15 | @SerializedName("id") 16 | private final int remoteId; 17 | 18 | EUserGroupTypeV1Dto(int remoteId) { 19 | this.remoteId = remoteId; 20 | } 21 | 22 | public int getRemoteId() { 23 | return remoteId; 24 | } 25 | 26 | @NonNull 27 | public static EUserGroupTypeV1Dto findByRemoteId(int remoteId) { 28 | for (final var value : values()) { 29 | if (value.getRemoteId() == remoteId) { 30 | return value; 31 | } 32 | } 33 | 34 | throw new NoSuchElementException("Could not find " + EUserGroupTypeV1Dto.class.getSimpleName() + " with remoteId " + remoteId); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/model/FetchRowResponseV1Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.io.Serializable; 8 | import java.time.Instant; 9 | import java.util.List; 10 | 11 | import it.niedermann.nextcloud.tables.remote.shared.model.DataResponseDto; 12 | 13 | public record FetchRowResponseV1Dto( 14 | @SerializedName("id") 15 | @Nullable Long remoteId, 16 | @Nullable String createdBy, 17 | @Nullable Instant createdAt, 18 | @Nullable String lastEditBy, 19 | @Nullable Instant lastEditAt, 20 | @Nullable List data 21 | ) implements Serializable { 22 | } 23 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/model/UpdateColumnResponseV1Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.io.Serializable; 6 | 7 | import it.niedermann.nextcloud.tables.remote.shared.model.RemoteDto; 8 | 9 | public record UpdateColumnResponseV1Dto( 10 | @SerializedName("id") 11 | Long remoteId 12 | ) implements Serializable, RemoteDto { 13 | } 14 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/model/UpdateRowRequestV1Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.google.gson.JsonElement; 6 | 7 | import java.io.Serializable; 8 | import java.util.Map; 9 | 10 | public record UpdateRowRequestV1Dto( 11 | @NonNull Map data 12 | ) implements Serializable { 13 | } 14 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV1/model/UpdateRowResponseV1Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV1.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public record UpdateRowResponseV1Dto( 6 | @SerializedName("id") 7 | long remoteId 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/adapter/ENodeTypeV2Adapter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.adapter; 2 | 3 | import com.google.gson.JsonDeserializationContext; 4 | import com.google.gson.JsonDeserializer; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonParseException; 7 | import com.google.gson.JsonPrimitive; 8 | import com.google.gson.JsonSerializationContext; 9 | import com.google.gson.JsonSerializer; 10 | 11 | import java.lang.reflect.Type; 12 | import java.util.Optional; 13 | 14 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.ENodeTypeV2Dto; 15 | 16 | 17 | public class ENodeTypeV2Adapter implements JsonSerializer, JsonDeserializer { 18 | 19 | @Override 20 | public ENodeTypeV2Dto deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 21 | return Optional.ofNullable(json) 22 | .map(JsonElement::getAsString) 23 | .map(ENodeTypeV2Dto::findByString) 24 | .orElse(null); 25 | } 26 | 27 | @Override 28 | public JsonElement serialize(ENodeTypeV2Dto src, Type typeOfSrc, JsonSerializationContext context) { 29 | return Optional.ofNullable(src) 30 | .map(ENodeTypeV2Dto::toString) 31 | .map(JsonPrimitive::new) 32 | .orElse(null); 33 | } 34 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/adapter/EUserGroupV2Adapter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.adapter; 2 | 3 | import com.google.gson.JsonDeserializationContext; 4 | import com.google.gson.JsonDeserializer; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonParseException; 7 | import com.google.gson.JsonPrimitive; 8 | import com.google.gson.JsonSerializationContext; 9 | import com.google.gson.JsonSerializer; 10 | 11 | import java.lang.reflect.Type; 12 | import java.util.Optional; 13 | 14 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.EUserGroupTypeV2Dto; 15 | 16 | public class EUserGroupV2Adapter implements JsonSerializer, JsonDeserializer { 17 | 18 | @Override 19 | public EUserGroupTypeV2Dto deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 20 | return Optional.ofNullable(json) 21 | .map(JsonElement::getAsInt) 22 | .map(EUserGroupTypeV2Dto::findByRemoteId) 23 | .orElse(null); 24 | } 25 | 26 | @Override 27 | public JsonElement serialize(EUserGroupTypeV2Dto src, Type typeOfSrc, JsonSerializationContext context) { 28 | return Optional.ofNullable(src) 29 | .map(EUserGroupTypeV2Dto::getRemoteId) 30 | .map(JsonPrimitive::new) 31 | .orElse(null); 32 | } 33 | } -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/creators/ColumnCreator.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.creators; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.nextcloud.android.sso.model.ocs.OcsResponse; 6 | 7 | import it.niedermann.nextcloud.tables.remote.tablesV2.TablesV2API; 8 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.ColumnV2Dto; 9 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.CreateColumnResponseV2Dto; 10 | import retrofit2.Call; 11 | 12 | public interface ColumnCreator { 13 | 14 | Call> createColumn(@NonNull TablesV2API tablesV2API, 15 | long tableRemoteId, 16 | @NonNull ColumnV2Dto column); 17 | } 18 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/creators/NoOpCreator.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.creators; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.nextcloud.android.sso.model.ocs.OcsResponse; 6 | 7 | import it.niedermann.nextcloud.tables.remote.tablesV2.TablesV2API; 8 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.ColumnV2Dto; 9 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.CreateColumnResponseV2Dto; 10 | import retrofit2.Call; 11 | 12 | public class NoOpCreator implements ColumnCreator { 13 | 14 | @Override 15 | public Call> createColumn(@NonNull TablesV2API tablesV2API, 16 | long tableRemoteId, 17 | @NonNull ColumnV2Dto column) { 18 | throw new UnsupportedOperationException("Can not create columns for the given type."); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/CreateColumnResponseV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.io.Serializable; 8 | 9 | import it.niedermann.nextcloud.tables.remote.shared.model.RemoteDto; 10 | 11 | public record CreateColumnResponseV2Dto( 12 | @SerializedName("id") 13 | @Nullable Long remoteId 14 | ) implements Serializable, RemoteDto { 15 | } 16 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/CreateRowResponseV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.io.Serializable; 8 | import java.time.Instant; 9 | import java.util.List; 10 | 11 | import it.niedermann.nextcloud.tables.remote.shared.model.DataResponseDto; 12 | 13 | public record CreateRowResponseV2Dto( 14 | @SerializedName("id") 15 | @Nullable Long remoteId, 16 | @Nullable String createdBy, 17 | @Nullable Instant createdAt, 18 | @Nullable String lastEditBy, 19 | @Nullable Instant lastEditAt, 20 | @Nullable List data 21 | ) implements Serializable { 22 | } 23 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/CreateRowV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.google.gson.JsonElement; 6 | 7 | import java.io.Serializable; 8 | import java.util.Map; 9 | 10 | public record CreateRowV2Dto( 11 | @NonNull Map data 12 | ) implements Serializable { 13 | } 14 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/ENodeCollectionV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | public enum ENodeCollectionV2Dto { 6 | TABLES("tables"), 7 | ; 8 | 9 | private final String nodeCollection; 10 | 11 | ENodeCollectionV2Dto(@NonNull String nodeCollection) { 12 | this.nodeCollection = nodeCollection; 13 | } 14 | 15 | @NonNull 16 | @Override 17 | public String toString() { 18 | return this.nodeCollection; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/ENodeTypeV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import java.util.NoSuchElementException; 7 | 8 | public enum ENodeTypeV2Dto { 9 | TABLE("table", 0), 10 | VIEW("view", 1), 11 | ; 12 | 13 | public final String nodeType; 14 | public final int id; 15 | 16 | ENodeTypeV2Dto(@NonNull String nodeType, int id) { 17 | this.nodeType = nodeType; 18 | this.id = id; 19 | } 20 | 21 | @NonNull 22 | @Override 23 | public String toString() { 24 | return this.nodeType; 25 | } 26 | 27 | public static ENodeTypeV2Dto findByString(@Nullable String stringRepresentation) { 28 | for (final var value : values()) { 29 | if (value.nodeType.equals(stringRepresentation)) { 30 | return value; 31 | } 32 | } 33 | 34 | throw new NoSuchElementException("Unknown " + ENodeTypeV2Dto.class.getSimpleName() + ": \"" + stringRepresentation + "\""); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/EPermissionV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | public enum EPermissionV2Dto { 4 | READ, 5 | CREATE, 6 | UPDATE, 7 | DELETE, 8 | MANAGE, 9 | } 10 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/EUserGroupTypeV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | public enum EUserGroupTypeV2Dto { 6 | USER(0), 7 | GROUP(1), 8 | TEAMS(7), 9 | ; 10 | 11 | private final int remoteId; 12 | 13 | EUserGroupTypeV2Dto(int remoteId) { 14 | this.remoteId = remoteId; 15 | } 16 | 17 | public int getRemoteId() { 18 | return remoteId; 19 | } 20 | 21 | public static EUserGroupTypeV2Dto findByRemoteId(int remoteId) { 22 | for (final var value : values()) { 23 | if (value.remoteId == remoteId) { 24 | return value; 25 | } 26 | } 27 | 28 | throw new NoSuchElementException("Could not find " + EUserGroupTypeV2Dto.class.getSimpleName() + " with remoteId " + remoteId); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/OnSharePermissionV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import java.io.Serializable; 6 | 7 | public record OnSharePermissionV2Dto( 8 | @Nullable Boolean read, 9 | @Nullable Boolean create, 10 | @Nullable Boolean update, 11 | @Nullable Boolean delete, 12 | @Nullable Boolean manage 13 | ) implements Serializable { 14 | } 15 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/SelectionOptionV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.io.Serializable; 8 | 9 | public record SelectionOptionV2Dto( 10 | @SerializedName("id") 11 | @Nullable Long remoteId, 12 | @Nullable String label 13 | ) implements Serializable { 14 | } 15 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/TableV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.io.Serializable; 9 | import java.time.Instant; 10 | import java.util.Locale; 11 | 12 | import it.niedermann.nextcloud.tables.remote.shared.model.RemoteDto; 13 | 14 | public record TableV2Dto( 15 | @SerializedName("id") 16 | @Nullable Long remoteId, 17 | @NonNull String title, 18 | @NonNull String emoji, 19 | @Nullable String description, 20 | @Nullable String ownership, 21 | @Nullable String ownerDisplayName, 22 | @Nullable String createdBy, 23 | @Nullable Instant createdAt, 24 | @Nullable String lastEditBy, 25 | @Nullable Instant lastEditAt, 26 | @Nullable Boolean archived, 27 | @Nullable Boolean favorite, 28 | @Nullable Boolean isShared, 29 | @Nullable OnSharePermissionV2Dto onSharePermissions 30 | ) implements Serializable, RemoteDto { 31 | 32 | @NonNull 33 | @Override 34 | public String toString() { 35 | return String.format(Locale.getDefault(), "%s %s", emoji(), title()).trim(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/UserGroupV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.io.Serializable; 8 | 9 | public record UserGroupV2Dto( 10 | @SerializedName("id") 11 | String remoteId, 12 | @NonNull 13 | String key, 14 | EUserGroupTypeV2Dto type 15 | ) implements Serializable { 16 | } 17 | -------------------------------------------------------------------------------- /remote/src/main/java/it/niedermann/nextcloud/tables/remote/tablesV2/model/columns/CreateDateTimeColumnV2Dto.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.remote.tablesV2.model.columns; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.Objects; 6 | 7 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.ColumnV2Dto; 8 | 9 | public class CreateDateTimeColumnV2Dto extends CreateColumnV2Dto { 10 | 11 | private final String datetimeDefault; 12 | 13 | public CreateDateTimeColumnV2Dto(long tableRemoteId, @NonNull ColumnV2Dto column) { 14 | super(tableRemoteId, column); 15 | this.datetimeDefault = column.datetimeDefault(); 16 | } 17 | 18 | public String getDatetimeDefault() { 19 | return datetimeDefault; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) return true; 25 | if (o == null || getClass() != o.getClass()) return false; 26 | if (!super.equals(o)) return false; 27 | CreateDateTimeColumnV2Dto that = (CreateDateTimeColumnV2Dto) o; 28 | return Objects.equals(datetimeDefault, that.datetimeDefault); 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | return Objects.hash(super.hashCode(), datetimeDefault); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /remote/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nextcloud Tables is not installed 4 | Nextcloud Tables is not enabled 5 | Your Nextcloud instance is currently in maintenance mode 6 | Your server responded with an invalid response 7 | Your Nextcloud Tables app version is not supported 8 | Your Nextcloud version is not supported 9 | An unknown error occurred 10 | You are currently offline. 11 | -------------------------------------------------------------------------------- /repository/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /repository/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/repository/consumer-rules.pro -------------------------------------------------------------------------------- /repository/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.google.gson.** { *; } 2 | 3 | # Add project specific ProGuard rules here. 4 | # You can control the set of applied configuration files using the 5 | # proguardFiles setting in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # If your project uses WebView with JS, uncomment the following 11 | # and specify the fully qualified class name to the JavaScript interface 12 | # class: 13 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 14 | # public *; 15 | #} 16 | 17 | # Uncomment this to preserve the line number information for 18 | # debugging stack traces. 19 | #-keepattributes SourceFile,LineNumberTable 20 | 21 | # If you keep the line number information, uncomment this to 22 | # hide the original source file name. 23 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /repository/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/NoOpDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 6 | import it.niedermann.nextcloud.tables.database.model.FullData; 7 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 8 | 9 | public class NoOpDefaultSupplier extends DefaultValueSupplier { 10 | 11 | @Override 12 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 13 | // No op 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/datetime/DateDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.datetime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.time.LocalDate; 6 | import java.util.Optional; 7 | 8 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 9 | import it.niedermann.nextcloud.tables.database.model.FullData; 10 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 11 | 12 | public class DateDefaultSupplier extends DefaultValueSupplier { 13 | 14 | private static final String DEFAULT_TODAY = "today"; 15 | 16 | @Override 17 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 18 | final var value = fullData.getData().getValue().getDateValue(); 19 | 20 | if (value == null) { 21 | final var defaultValue = fullColumn.getColumn().getDefaultValue().getStringValue(); 22 | Optional.ofNullable(defaultValue) 23 | .filter(DEFAULT_TODAY::equals) 24 | .map(str -> LocalDate.now()) 25 | .ifPresent(fullData.getData().getValue()::setDateValue); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/datetime/DateTimeDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.datetime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.time.Instant; 6 | import java.util.Optional; 7 | 8 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 9 | import it.niedermann.nextcloud.tables.database.model.FullData; 10 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 11 | 12 | public class DateTimeDefaultSupplier extends DefaultValueSupplier { 13 | 14 | private static final String DEFAULT_NOW = "now"; 15 | 16 | @Override 17 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 18 | final var value = fullData.getData().getValue().getInstantValue(); 19 | 20 | if (value == null) { 21 | final var defaultValue = fullColumn.getColumn().getDefaultValue().getStringValue(); 22 | Optional.ofNullable(defaultValue) 23 | .filter(DEFAULT_NOW::equals) 24 | .map(str -> Instant.now()) 25 | .ifPresent(fullData.getData().getValue()::setInstantValue); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/datetime/TimeDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.datetime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.time.LocalTime; 6 | import java.util.Optional; 7 | 8 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 9 | import it.niedermann.nextcloud.tables.database.model.FullData; 10 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 11 | 12 | public class TimeDefaultSupplier extends DefaultValueSupplier { 13 | 14 | private static final String DEFAULT_NOW = "now"; 15 | 16 | @Override 17 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 18 | final var value = fullData.getData().getValue().getTimeValue(); 19 | 20 | if (value == null) { 21 | final var defaultValue = fullColumn.getColumn().getDefaultValue().getStringValue(); 22 | Optional.ofNullable(defaultValue) 23 | .filter(DEFAULT_NOW::equals) 24 | .map(str -> LocalTime.now()) 25 | .ifPresent(fullData.getData().getValue()::setTimeValue); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/number/NumberDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.number; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 6 | import it.niedermann.nextcloud.tables.database.model.FullData; 7 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 8 | 9 | public class NumberDefaultSupplier extends DefaultValueSupplier { 10 | 11 | @Override 12 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 13 | final var value = fullData.getData().getValue().getDoubleValue(); 14 | 15 | if (value == null) { 16 | final var defaultValue = fullColumn.getColumn().getDefaultValue().getDoubleValue(); 17 | fullData.getData().getValue().setDoubleValue(defaultValue); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/number/NumberStarsDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.number; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.Optional; 6 | 7 | import it.niedermann.nextcloud.tables.database.entity.Column; 8 | import it.niedermann.nextcloud.tables.database.entity.Data; 9 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 10 | import it.niedermann.nextcloud.tables.database.model.FullData; 11 | import it.niedermann.nextcloud.tables.database.model.Value; 12 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 13 | 14 | public class NumberStarsDefaultSupplier extends DefaultValueSupplier { 15 | 16 | @Override 17 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 18 | final var value = Optional 19 | .of(fullData.getData()) 20 | .map(Data::getValue) 21 | .map(Value::getDoubleValue); 22 | 23 | if (value.isEmpty()) { 24 | Optional 25 | .of(fullColumn.getColumn()) 26 | .map(Column::getDefaultValue) 27 | .map(Value::getDoubleValue) 28 | .map(Math::ceil) 29 | .ifPresent(fullData.getData().getValue()::setDoubleValue); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/selection/SelectionCheckDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.selection; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.Optional; 6 | 7 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 8 | import it.niedermann.nextcloud.tables.database.model.FullData; 9 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 10 | 11 | public class SelectionCheckDefaultSupplier extends DefaultValueSupplier { 12 | 13 | @Override 14 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 15 | final var value = fullData.getData().getValue().getBooleanValue(); 16 | 17 | if (value == null) { 18 | final var defaultValue = fullColumn.getColumn().getDefaultValue().getBooleanValue(); 19 | Optional.ofNullable(defaultValue) 20 | .ifPresent(fullData.getData().getValue()::setBooleanValue); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/selection/SelectionDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.selection; 2 | 3 | import static java.util.function.Predicate.not; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 11 | import it.niedermann.nextcloud.tables.database.model.FullData; 12 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 13 | 14 | public class SelectionDefaultSupplier extends DefaultValueSupplier { 15 | 16 | @Override 17 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 18 | final var value = fullData.getSelectionOptions(); 19 | 20 | if (value == null || value.isEmpty()) { 21 | final var defaultValue = fullColumn.getDefaultSelectionOptions(); 22 | Optional.of(defaultValue) 23 | .filter(not(List::isEmpty)) 24 | .flatMap(selectionOptions -> selectionOptions.stream().findAny()) 25 | .map(List::of) 26 | .ifPresent(fullData::setSelectionOptions); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/selection/SelectionMultiDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.selection; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.Optional; 6 | 7 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 8 | import it.niedermann.nextcloud.tables.database.model.FullData; 9 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 10 | 11 | public class SelectionMultiDefaultSupplier extends DefaultValueSupplier { 12 | 13 | @Override 14 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 15 | final var value = fullData.getSelectionOptions(); 16 | 17 | if (value == null || value.isEmpty()) { 18 | final var defaultValue = fullColumn.getDefaultSelectionOptions(); 19 | Optional.ofNullable(defaultValue) 20 | .ifPresent(fullData::setSelectionOptions); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/defaults/supplier/text/TextDefaultSupplier.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.defaults.supplier.text; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.Optional; 6 | 7 | import it.niedermann.nextcloud.tables.database.entity.Column; 8 | import it.niedermann.nextcloud.tables.database.model.FullColumn; 9 | import it.niedermann.nextcloud.tables.database.model.FullData; 10 | import it.niedermann.nextcloud.tables.database.model.Value; 11 | import it.niedermann.nextcloud.tables.repository.defaults.DefaultValueSupplier; 12 | 13 | public class TextDefaultSupplier extends DefaultValueSupplier { 14 | 15 | @Override 16 | protected void applyDefaultValue(@NonNull FullColumn fullColumn, @NonNull FullData fullData) { 17 | final var value = fullData.getData().getValue(); 18 | final var strValue = Optional.ofNullable(value.getStringValue()); 19 | 20 | if (strValue.isEmpty()) { 21 | Optional.of(fullColumn.getColumn()) 22 | .map(Column::getDefaultValue) 23 | .map(Value::getStringValue) 24 | .ifPresent(value::setStringValue); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/exception/AccountAlreadyImportedException.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.exception; 2 | 3 | import android.database.sqlite.SQLiteConstraintException; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | public class AccountAlreadyImportedException extends AccountNotCreatedException { 8 | 9 | public AccountAlreadyImportedException(@NonNull SQLiteConstraintException cause) { 10 | super(cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/exception/AccountNotCreatedException.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.exception; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | public class AccountNotCreatedException extends RuntimeException { 6 | 7 | public AccountNotCreatedException() { 8 | super(); 9 | } 10 | 11 | public AccountNotCreatedException(@Nullable Throwable cause) { 12 | super(cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/exception/InsufficientPermissionException.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.exception; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.EPermissionV2Dto; 7 | 8 | public class InsufficientPermissionException extends Exception { 9 | 10 | private final EPermissionV2Dto missingPermission; 11 | 12 | public InsufficientPermissionException(@NonNull EPermissionV2Dto missingPermission) { 13 | this.missingPermission = missingPermission; 14 | } 15 | 16 | @NonNull 17 | public EPermissionV2Dto getMissingPermission() { 18 | return missingPermission; 19 | } 20 | 21 | @Nullable 22 | @Override 23 | public String getMessage() { 24 | return String.valueOf(missingPermission); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/SyncScheduler.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.AnyThread; 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import java.util.concurrent.CompletableFuture; 10 | 11 | import it.niedermann.nextcloud.tables.database.entity.Account; 12 | import it.niedermann.nextcloud.tables.repository.sync.report.SyncStatusReporter; 13 | import it.niedermann.nextcloud.tables.repository.sync.treesync.TreeSyncScheduler; 14 | 15 | public interface SyncScheduler { 16 | 17 | @AnyThread 18 | CompletableFuture scheduleSynchronization(@NonNull Account account, 19 | @NonNull Scope scope, 20 | @Nullable SyncStatusReporter reporter); 21 | 22 | class Factory { 23 | 24 | private final SyncScheduler defaultSyncScheduler; 25 | 26 | public Factory(@NonNull Context context) { 27 | defaultSyncScheduler = new TreeSyncScheduler(context.getApplicationContext()); 28 | } 29 | 30 | @NonNull 31 | public SyncScheduler create() { 32 | return defaultSyncScheduler; 33 | } 34 | } 35 | 36 | enum Scope { 37 | PUSH_ONLY, 38 | PUSH_AND_PULL 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/RemoteMapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.Collection; 6 | import java.util.List; 7 | import java.util.Objects; 8 | 9 | public interface RemoteMapper { 10 | 11 | DtoType toDto(EntityType entity); 12 | 13 | EntityType toEntity(DtoType dto); 14 | 15 | @NonNull 16 | default List toDtoList(@NonNull Collection entities) { 17 | return entities 18 | .stream() 19 | .filter(Objects::nonNull) 20 | .map(this::toDto) 21 | .toList(); 22 | } 23 | 24 | @NonNull 25 | default List toEntityList(@NonNull Collection dtos) { 26 | return dtos 27 | .stream() 28 | .filter(Objects::nonNull) 29 | .map(this::toEntity) 30 | .toList(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/ocs/OcsSearchProviderMapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.ocs; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.factory.Mappers; 6 | 7 | import it.niedermann.nextcloud.tables.database.entity.SearchProvider; 8 | import it.niedermann.nextcloud.tables.remote.ocs.model.OcsSearchProvider; 9 | import it.niedermann.nextcloud.tables.repository.sync.mapper.RemoteMapper; 10 | 11 | @Mapper 12 | public interface OcsSearchProviderMapper extends RemoteMapper { 13 | 14 | OcsSearchProviderMapper INSTANCE = Mappers.getMapper(OcsSearchProviderMapper.class); 15 | 16 | @Override 17 | OcsSearchProvider toDto(SearchProvider entity); 18 | 19 | @Mapping(target = "id", ignore = true) 20 | @Mapping(target = "accountId", ignore = true) 21 | @Override 22 | SearchProvider toEntity(OcsSearchProvider dto); 23 | } 24 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/ocs/OcsVersionMapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.ocs; 2 | 3 | import com.nextcloud.android.sso.model.ocs.OcsCapabilitiesResponse.OcsVersion; 4 | 5 | import org.mapstruct.InheritInverseConfiguration; 6 | import org.mapstruct.Mapper; 7 | import org.mapstruct.Mapping; 8 | import org.mapstruct.factory.Mappers; 9 | 10 | import it.niedermann.nextcloud.tables.database.model.Version; 11 | import it.niedermann.nextcloud.tables.repository.sync.mapper.RemoteMapper; 12 | 13 | @Mapper 14 | public interface OcsVersionMapper extends RemoteMapper { 15 | 16 | OcsVersionMapper INSTANCE = Mappers.getMapper(OcsVersionMapper.class); 17 | 18 | @Mapping(source = "version", target = "string") 19 | @Mapping(source = "patch", target = "macro") 20 | @Mapping(target = "edition", ignore = true) 21 | @Mapping(target = "extendedSupport", ignore = true) 22 | OcsVersion toDto(Version entity); 23 | 24 | @InheritInverseConfiguration 25 | Version toEntity(OcsVersion dto); 26 | } 27 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/tablesV2/EUserGroupTypeV2Mapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.tablesV2; 2 | 3 | import org.mapstruct.InheritInverseConfiguration; 4 | import org.mapstruct.Mapper; 5 | import org.mapstruct.MappingConstants; 6 | import org.mapstruct.ValueMapping; 7 | import org.mapstruct.factory.Mappers; 8 | 9 | import it.niedermann.nextcloud.tables.database.model.EUserGroupType; 10 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.EUserGroupTypeV2Dto; 11 | import it.niedermann.nextcloud.tables.repository.sync.mapper.RemoteMapper; 12 | 13 | @Mapper 14 | public interface EUserGroupTypeV2Mapper extends RemoteMapper { 15 | 16 | EUserGroupTypeV2Mapper INSTANCE = Mappers.getMapper(EUserGroupTypeV2Mapper.class); 17 | 18 | @ValueMapping(source = "TEAM", target = "TEAMS") 19 | @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.THROW_EXCEPTION) 20 | @ValueMapping(source = MappingConstants.NULL, target = MappingConstants.THROW_EXCEPTION) 21 | @Override 22 | EUserGroupTypeV2Dto toDto(EUserGroupType entity); 23 | 24 | @InheritInverseConfiguration 25 | @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "UNKNOWN") 26 | @ValueMapping(source = MappingConstants.NULL, target = "UNKNOWN") 27 | @Override 28 | EUserGroupType toEntity(EUserGroupTypeV2Dto dto); 29 | } 30 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/tablesV2/OnSharePermissionV2Mapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.tablesV2; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.NullValueMappingStrategy; 5 | import org.mapstruct.factory.Mappers; 6 | 7 | import it.niedermann.nextcloud.tables.database.entity.OnSharePermission; 8 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.OnSharePermissionV2Dto; 9 | import it.niedermann.nextcloud.tables.repository.sync.mapper.RemoteMapper; 10 | 11 | @Mapper(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT) 12 | public interface OnSharePermissionV2Mapper extends RemoteMapper { 13 | 14 | OnSharePermissionV2Mapper INSTANCE = Mappers.getMapper(OnSharePermissionV2Mapper.class); 15 | 16 | @Override 17 | OnSharePermissionV2Dto toDto(OnSharePermission entity); 18 | 19 | @Override 20 | OnSharePermission toEntity(OnSharePermissionV2Dto dto); 21 | } 22 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/tablesV2/SelectionOptionV2Mapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.tablesV2; 2 | 3 | import org.mapstruct.InheritInverseConfiguration; 4 | import org.mapstruct.Mapper; 5 | import org.mapstruct.Mapping; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | import it.niedermann.nextcloud.tables.database.entity.SelectionOption; 9 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.SelectionOptionV2Dto; 10 | import it.niedermann.nextcloud.tables.repository.sync.mapper.RemoteMapper; 11 | 12 | @Mapper 13 | public interface SelectionOptionV2Mapper extends RemoteMapper { 14 | 15 | SelectionOptionV2Mapper INSTANCE = Mappers.getMapper(SelectionOptionV2Mapper.class); 16 | 17 | @Override 18 | SelectionOptionV2Dto toDto(SelectionOption entity); 19 | 20 | @Mapping(target = "id", ignore = true) 21 | @Mapping(target = "columnId", ignore = true) 22 | @InheritInverseConfiguration 23 | SelectionOption toEntity(SelectionOptionV2Dto dto); 24 | } 25 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/tablesV2/TableV2Mapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.tablesV2; 2 | 3 | import org.mapstruct.InheritInverseConfiguration; 4 | import org.mapstruct.Mapper; 5 | import org.mapstruct.Mapping; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | import it.niedermann.nextcloud.tables.database.entity.Table; 9 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.TableV2Dto; 10 | import it.niedermann.nextcloud.tables.repository.sync.mapper.RemoteMapper; 11 | 12 | @Mapper(uses = OnSharePermissionV2Mapper.class) 13 | public interface TableV2Mapper extends RemoteMapper { 14 | 15 | TableV2Mapper INSTANCE = Mappers.getMapper(TableV2Mapper.class); 16 | 17 | @Mapping(source = "shared", target = "isShared") 18 | @Mapping(source = "onSharePermission", target = "onSharePermissions") 19 | @Override 20 | TableV2Dto toDto(Table entity); 21 | 22 | @Mapping(target = "id", ignore = true) 23 | @Mapping(target = "accountId", ignore = true) 24 | @Mapping(target = "status", ignore = true) 25 | @Mapping(target = "ETag", ignore = true) 26 | @Mapping(target = "synchronizationContext", ignore = true) 27 | @Mapping(target = "currentRow", ignore = true) 28 | @InheritInverseConfiguration 29 | @Override 30 | Table toEntity(TableV2Dto dto); 31 | } 32 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/mapper/tablesV2/UserGroupV2Mapper.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.tablesV2; 2 | 3 | import org.mapstruct.InheritInverseConfiguration; 4 | import org.mapstruct.Mapper; 5 | import org.mapstruct.Mapping; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | import it.niedermann.nextcloud.tables.database.entity.UserGroup; 9 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.UserGroupV2Dto; 10 | import it.niedermann.nextcloud.tables.repository.sync.mapper.RemoteMapper; 11 | 12 | @Mapper(uses = EUserGroupTypeV2Mapper.class) 13 | public interface UserGroupV2Mapper extends RemoteMapper { 14 | 15 | UserGroupV2Mapper INSTANCE = Mappers.getMapper(UserGroupV2Mapper.class); 16 | 17 | @Mapping(source = "displayName", target = "key") 18 | @Override 19 | UserGroupV2Dto toDto(UserGroup entity); 20 | 21 | @Mapping(target = "id", ignore = true) 22 | @Mapping(target = "accountId", ignore = true) 23 | @InheritInverseConfiguration 24 | @Override 25 | UserGroup toEntity(UserGroupV2Dto dto); 26 | } 27 | -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/report/LiveDataReporter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.report; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.lifecycle.LiveData; 5 | 6 | import it.niedermann.nextcloud.tables.database.entity.Account; 7 | 8 | public class LiveDataReporter extends LiveData implements SyncStatusReporter { 9 | 10 | /// Given [#setValue] can only happen on the main thread and [#postValue] can omit intermediate status, 11 | /// we will ensure that we always rely on the latest [SyncStatus] before applying the [Reducer] logic 12 | /// by storing a duplicate of the [SyncStatus] locally to apply the [Reducer] logic on, but not emitting all intermediates. 13 | private SyncStatus currentSyncStatus; 14 | 15 | public LiveDataReporter(@NonNull Account account) { 16 | super(new SyncStatus(account)); 17 | this.currentSyncStatus = getValue(); 18 | } 19 | 20 | @Override 21 | public boolean report(@NonNull Reducer reducer) { 22 | synchronized (this) { 23 | if (currentSyncStatus.isFinished()) { 24 | return false; 25 | } 26 | 27 | currentSyncStatus = reducer.apply(currentSyncStatus); 28 | postValue(currentSyncStatus); 29 | } 30 | 31 | return true; 32 | } 33 | } -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/report/SyncStatusReporter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.report; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.function.Function; 6 | 7 | @FunctionalInterface 8 | public interface SyncStatusReporter { 9 | /// @return whether or not the report got reduced 10 | boolean report(@NonNull Reducer reducer); 11 | 12 | @FunctionalInterface 13 | interface Reducer extends Function { 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /repository/src/main/java/it/niedermann/nextcloud/tables/repository/sync/treesync/SyncAdapter.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.treesync; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.concurrent.CompletableFuture; 6 | 7 | import it.niedermann.nextcloud.tables.database.entity.AbstractEntity; 8 | import it.niedermann.nextcloud.tables.database.entity.Account; 9 | 10 | interface SyncAdapter { 11 | 12 | @NonNull 13 | CompletableFuture pushLocalCreations(@NonNull Account account, @NonNull TParentEntity parentEntity); 14 | 15 | @NonNull 16 | CompletableFuture pushLocalUpdates(@NonNull Account account, @NonNull TParentEntity parentEntity); 17 | 18 | @NonNull 19 | CompletableFuture pushLocalDeletions(@NonNull Account account, @NonNull TParentEntity parentEntity); 20 | 21 | @NonNull 22 | CompletableFuture pushChildChangesWithoutChangedParent(@NonNull Account account); 23 | 24 | @NonNull 25 | CompletableFuture pullRemoteChanges(@NonNull Account account, @NonNull TParentEntity parentEntity); 26 | } 27 | -------------------------------------------------------------------------------- /repository/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sync only on Wi-Fi 4 | Background synchronization 5 | Theme 6 | 7 | sync_on_wifi_only 8 | background_sync 9 | background_sync_last 10 | theme 11 | 12 | 13 | -1 14 | 1 15 | 2 16 | 17 | 18 | 19 | System Default 20 | Light 21 | Dark 22 | 23 | -------------------------------------------------------------------------------- /repository/src/test/java/it/niedermann/nextcloud/tables/repository/sync/mapper/tablesV2/SelectionOptionV2MapperTest.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.repository.sync.mapper.tablesV2; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import it.niedermann.nextcloud.tables.database.entity.SelectionOption; 9 | import it.niedermann.nextcloud.tables.remote.tablesV2.model.SelectionOptionV2Dto; 10 | 11 | public class SelectionOptionV2MapperTest { 12 | 13 | private SelectionOptionV2Mapper mapper; 14 | 15 | @Before 16 | public void setup() { 17 | this.mapper = SelectionOptionV2Mapper.INSTANCE; 18 | } 19 | 20 | @Test 21 | public void toDto() { 22 | 23 | final var entity = new SelectionOption(); 24 | entity.setRemoteId(4711L); 25 | entity.setLabel("foo"); 26 | 27 | final var dto = mapper.toDto(entity); 28 | 29 | assertEquals(Long.valueOf(4711L), dto.remoteId()); 30 | assertEquals("foo", dto.label()); 31 | } 32 | 33 | @Test 34 | public void toEntity() { 35 | 36 | final var dto = new SelectionOptionV2Dto(4711L, "foo"); 37 | 38 | final var entity = mapper.toEntity(dto); 39 | 40 | assertEquals(Long.valueOf(4711L), entity.getRemoteId()); 41 | assertEquals("foo", entity.getLabel()); 42 | } 43 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { url "https://jitpack.io" } 14 | } 15 | } 16 | rootProject.name = "Nextcloud Tables" 17 | include ':app' 18 | include ':ui' 19 | include ':repository' 20 | include ':remote' 21 | include ':database' 22 | include ':shared' 23 | -------------------------------------------------------------------------------- /shared/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /shared/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | namespace 'it.niedermann.nextcloud.tables.shared' 7 | compileSdk 35 8 | 9 | defaultConfig { 10 | minSdk 24 11 | 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | consumerProguardFiles "consumer-rules.pro" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | compileOptions { 23 | coreLibraryDesugaringEnabled true 24 | sourceCompatibility JavaVersion.VERSION_17 25 | targetCompatibility JavaVersion.VERSION_17 26 | } 27 | buildFeatures { 28 | buildConfig true 29 | } 30 | } 31 | 32 | dependencies { 33 | coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$version_desugar" 34 | 35 | implementation 'androidx.appcompat:appcompat:1.7.0' 36 | 37 | testImplementation 'junit:junit:4.13.2' 38 | androidTestImplementation 'androidx.test.ext:junit:1.2.1' 39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' 40 | } -------------------------------------------------------------------------------- /shared/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /shared/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /shared/src/main/java/it/niedermann/nextcloud/tables/shared/Constants.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.shared; 2 | 3 | public class Constants { 4 | 5 | public static final int PROBABLE_ACCOUNT_COUNT = 1; 6 | } 7 | -------------------------------------------------------------------------------- /shared/src/main/java/it/niedermann/nextcloud/tables/shared/FeatureToggle.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.shared; 2 | 3 | public enum FeatureToggle { 4 | 5 | /// Some exceptions only affect a part of the app. Enabling [#STRICT_MODE] will always 6 | /// throw all exceptions to make the user aware of the fact that something went wrong. 7 | /// Disabling this [FeatureToggle] can lead to wrongly displayed data. 8 | STRICT_MODE(BuildConfig.DEBUG), 9 | EDIT_COLUMN(true), 10 | EDIT_USER_GROUPS(BuildConfig.DEBUG), 11 | CREATE_COLUMN(true), 12 | DELETE_COLUMN(true), 13 | SHARE_TABLE(BuildConfig.DEBUG), 14 | SEARCH_IN_TABLE(false), 15 | ; 16 | 17 | public final boolean enabled; 18 | 19 | FeatureToggle(boolean enabled) { 20 | this.enabled = enabled; 21 | } 22 | } -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /ui/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan-niedermann/nextcloud-tables/2a0816eb4fa810ec1a8f715ca3480e639adac954/ui/consumer-rules.pro -------------------------------------------------------------------------------- /ui/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /ui/src/androidTest/java/it/niedermann/nextcloud/tables/ui/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.ui; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | import androidx.test.platform.app.InstrumentationRegistry; 9 | 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("it.niedermann.nextcloud.tables.ui.test", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /ui/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/baseline_clear_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/baseline_restart_alt_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/baseline_star_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/baseline_star_border_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/selectable_star.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/view_emojipicker.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/view_stars.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 21 | 22 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/view_stars_single.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ui/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /ui/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4dp 4 | 8dp 5 | 16dp 6 | 20dp 7 | 24dp 8 | 32dp 9 | 40dp 10 | 48dp 11 | -------------------------------------------------------------------------------- /ui/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Clear stars 4 | Star %1$d of %2$d 5 | -------------------------------------------------------------------------------- /ui/src/test/java/it/niedermann/nextcloud/tables/ui/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package it.niedermann.nextcloud.tables.ui; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } --------------------------------------------------------------------------------