From 79a357537ba7982d15d0fd3d9a13849f7c2d942f Mon Sep 17 00:00:00 2001 From: Fr4nzD13trich Date: Thu, 20 Nov 2025 15:57:28 +0100 Subject: [PATCH] Branch added --- CHANGELOG.md | 61 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 8 +- Gemfile.lock | 113 +- LICENSES/LicenseRef-XTrademarks.txt | 49 + README.md | 13 +- REUSE.toml | 42 + SECURITY.md | 63 +- SETUP.md | 2 +- app/.gitignore | 2 +- app/build.gradle | 488 +- app/detekt.yml | 2 +- app/lint.xml | 22 +- .../81.json | 2 +- .../82.json | 1233 + .../83.json | 1245 + .../84.json | 1301 + .../85.json | 1301 + .../86.json | 1331 + .../87.json | 1337 + .../88.json | 1343 + .../89.json | 1349 + .../90.json | 1355 + .../91.json | 1195 + .../92.json | 1200 + .../93.json | 1205 + .../94.json | 1210 + .../95.json | 1215 + ...loud.client.ActivitiesActivityIT_empty.png | Bin ...ActivitiesActivityIT_empty_light_white.png | Bin ...loud.client.ActivitiesActivityIT_error.png | Bin ...ActivitiesActivityIT_error_light_white.png | Bin ...ud.client.ActivitiesActivityIT_loading.png | Bin ...client.ActivitiesActivityIT_openDrawer.png | Bin ...vitiesActivityIT_openDrawer_dark_black.png | Bin ...ivitiesActivityIT_openDrawer_dark_blue.png | Bin ...vitiesActivityIT_openDrawer_dark_white.png | Bin ...itiesActivityIT_openDrawer_light_black.png | Bin ...itiesActivityIT_openDrawer_light_white.png | Bin ...nt.ActivitiesActivityIT_showActivities.png | Bin ...sActivityIT_showActivities_light_white.png | Bin ...d.client.AuthenticatorActivityIT_login.png | Bin ...thenticatorActivityIT_login_dark_black.png | Bin ...uthenticatorActivityIT_login_dark_blue.png | Bin ...thenticatorActivityIT_login_dark_white.png | Bin ...henticatorActivityIT_login_light_black.png | Bin ...henticatorActivityIT_login_light_white.png | Bin ...tcloud.client.CommunityActivityIT_open.png | Bin 0 -> 56958 bytes ...nt.CommunityActivityIT_open_dark_black.png | Bin ...ent.CommunityActivityIT_open_dark_blue.png | Bin ...nt.CommunityActivityIT_open_dark_white.png | Bin ...t.CommunityActivityIT_open_light_black.png | Bin ...t.CommunityActivityIT_open_light_white.png | Bin ...nt.FileDisplayActivityIT_shareToCircle.png | Bin ...ent.FileDisplayActivityIT_showAccounts.png | Bin ...lient.FileDisplayActivityIT_showShares.png | Bin ...FileDisplayActivityScreenshotIT_drawer.png | Bin 0 -> 21927 bytes ...ActivityScreenshotIT_drawer_dark_black.png | Bin ...yActivityScreenshotIT_drawer_dark_blue.png | Bin ...ActivityScreenshotIT_drawer_dark_white.png | Bin ...ctivityScreenshotIT_drawer_light_black.png | Bin ...ctivityScreenshotIT_drawer_light_white.png | Bin ...t.FileDisplayActivityScreenshotIT_open.png | Bin ...ayActivityScreenshotIT_open_dark_black.png | Bin ...layActivityScreenshotIT_open_dark_blue.png | Bin ...ayActivityScreenshotIT_open_dark_white.png | Bin ...yActivityScreenshotIT_open_light_black.png | Bin ...yActivityScreenshotIT_open_light_white.png | Bin ...vityScreenshotIT_showMediaThenAllFiles.png | Bin ...xtcloud.client.FirstRunActivityIT_open.png | Bin ...ent.FirstRunActivityIT_open_dark_black.png | Bin ...ient.FirstRunActivityIT_open_dark_blue.png | Bin ...ent.FirstRunActivityIT_open_dark_white.png | Bin ...nt.FirstRunActivityIT_open_light_black.png | Bin ...nt.FirstRunActivityIT_open_light_white.png | Bin ...xtcloud.client.SettingsActivityIT_open.png | Bin 0 -> 35160 bytes ...ent.SettingsActivityIT_open_dark_black.png | Bin ...ient.SettingsActivityIT_open_dark_blue.png | Bin ...ent.SettingsActivityIT_open_dark_white.png | Bin ...nt.SettingsActivityIT_open_light_black.png | Bin ...nt.SettingsActivityIT_open_light_white.png | Bin ....SettingsActivityIT_showMnemonic_Error.png | Bin 0 -> 35224 bytes ...tivityIT_showMnemonic_Error_dark_black.png | Bin ...ctivityIT_showMnemonic_Error_dark_blue.png | Bin ...tivityIT_showMnemonic_Error_dark_white.png | Bin ...ivityIT_showMnemonic_Error_light_black.png | Bin ...ivityIT_showMnemonic_Error_light_white.png | Bin ...ud.client.SyncedFoldersActivityIT_open.png | Bin 0 -> 6086 bytes ...ent.SyncedFoldersActivityIT_openDrawer.png | Bin ...FoldersActivityIT_openDrawer_dark_blue.png | Bin ...oldersActivityIT_openDrawer_dark_white.png | Bin ...ldersActivityIT_openDrawer_light_white.png | Bin ...yncedFoldersActivityIT_open_dark_black.png | Bin ...SyncedFoldersActivityIT_open_dark_blue.png | Bin ...yncedFoldersActivityIT_open_dark_white.png | Bin ...ncedFoldersActivityIT_open_light_black.png | Bin ...ncedFoldersActivityIT_open_light_white.png | Bin ...FoldersActivityIT_showPowerCheckDialog.png | Bin 0 -> 9383 bytes ...ldersActivityIT_testSyncedFolderDialog.png | Bin 0 -> 30771 bytes ...tyIT_testSyncedFolderDialog_dark_black.png | Bin ...ityIT_testSyncedFolderDialog_dark_blue.png | Bin ...tyIT_testSyncedFolderDialog_dark_white.png | Bin ...yIT_testSyncedFolderDialog_light_black.png | Bin ...yIT_testSyncedFolderDialog_light_white.png | Bin ...ploadListActivityActivityIT_openDrawer.png | Bin 0 -> 22174 bytes ...tivityActivityIT_openDrawer_dark_black.png | Bin ...ctivityActivityIT_openDrawer_dark_blue.png | Bin ...tivityActivityIT_openDrawer_dark_white.png | Bin ...ivityActivityIT_openDrawer_light_black.png | Bin ...ivityActivityIT_openDrawer_light_white.png | Bin ...ud.client.etm.EtmActivityTest_accounts.png | Bin 0 -> 11115 bytes ...ud.client.etm.EtmActivityTest_overview.png | Bin 0 -> 14791 bytes .../com.nextcloud.ui.BitmapIT_glideSVG.png | Bin .../com.nextcloud.ui.BitmapIT_roundBitmap.png | Bin ...ty.ConflictsResolveActivityIT_keepBoth.png | Bin 0 -> 15123 bytes ...sResolveActivityIT_keepBoth_dark_black.png | Bin ...tsResolveActivityIT_keepBoth_dark_blue.png | Bin ...sResolveActivityIT_keepBoth_dark_white.png | Bin ...ResolveActivityIT_keepBoth_light_black.png | Bin ...ResolveActivityIT_keepBoth_light_white.png | Bin ...onflictsResolveActivityIT_keepExisting.png | Bin 0 -> 15162 bytes ...olveActivityIT_keepExisting_dark_black.png | Bin ...solveActivityIT_keepExisting_dark_blue.png | Bin ...olveActivityIT_keepExisting_dark_white.png | Bin ...lveActivityIT_keepExisting_light_black.png | Bin ...lveActivityIT_keepExisting_light_white.png | Bin ...ity.ConflictsResolveActivityIT_keepNew.png | Bin 0 -> 15156 bytes ...tsResolveActivityIT_keepNew_dark_black.png | Bin ...ctsResolveActivityIT_keepNew_dark_blue.png | Bin ...tsResolveActivityIT_keepNew_dark_white.png | Bin ...sResolveActivityIT_keepNew_light_black.png | Bin ...sResolveActivityIT_keepNew_light_white.png | Bin ...sResolveActivityIT_screenshotTextFiles.png | Bin 0 -> 14983 bytes ...ivityIT_screenshotTextFiles_dark_black.png | Bin ...tivityIT_screenshotTextFiles_dark_blue.png | Bin ...ivityIT_screenshotTextFiles_dark_white.png | Bin ...vityIT_screenshotTextFiles_light_black.png | Bin ...vityIT_screenshotTextFiles_light_white.png | Bin ...renceActivityIT_openContactsPreference.png | Bin 0 -> 16013 bytes ...tyIT_openContactsPreference_dark_black.png | Bin ...ityIT_openContactsPreference_dark_blue.png | Bin ...tyIT_openContactsPreference_dark_white.png | Bin ...yIT_openContactsPreference_light_black.png | Bin ...yIT_openContactsPreference_light_white.png | Bin ...y.ContactsPreferenceActivityIT_openVCF.png | Bin 0 -> 8113 bytes ...referenceActivityIT_openVCF_dark_black.png | Bin ...PreferenceActivityIT_openVCF_dark_blue.png | Bin ...referenceActivityIT_openVCF_dark_white.png | Bin ...eferenceActivityIT_openVCF_light_black.png | Bin ...eferenceActivityIT_openVCF_light_white.png | Bin ...i.activity.FolderPickerActivityIT_open.png | Bin 0 -> 13178 bytes ...FolderPickerActivityIT_open_dark_black.png | Bin ....FolderPickerActivityIT_open_dark_blue.png | Bin ...FolderPickerActivityIT_open_dark_white.png | Bin ...olderPickerActivityIT_open_light_black.png | Bin ...olderPickerActivityIT_open_light_white.png | Bin ...kerActivityIT_testChooseLocationAction.png | Bin 0 -> 13397 bytes ....FolderPickerActivityIT_testMoveOrCopy.png | Bin 0 -> 13705 bytes ...activity.ManageAccountsActivityIT_open.png | Bin 0 -> 10506 bytes ...nageAccountsActivityIT_open_dark_black.png | Bin ...anageAccountsActivityIT_open_dark_blue.png | Bin ...nageAccountsActivityIT_open_dark_white.png | Bin ...ageAccountsActivityIT_open_light_black.png | Bin ...ageAccountsActivityIT_open_light_white.png | Bin ...anageAccountsActivityIT_userInfoDetail.png | Bin 0 -> 23875 bytes ...tsActivityIT_userInfoDetail_dark_black.png | Bin ...ntsActivityIT_userInfoDetail_dark_blue.png | Bin ...tsActivityIT_userInfoDetail_dark_white.png | Bin ...sActivityIT_userInfoDetail_light_black.png | Bin ...sActivityIT_userInfoDetail_light_white.png | Bin ...activity.NotificationsActivityIT_empty.png | Bin ...tificationsActivityIT_empty_dark_black.png | Bin ...otificationsActivityIT_empty_dark_blue.png | Bin ...tificationsActivityIT_empty_dark_white.png | Bin ...ificationsActivityIT_empty_light_black.png | Bin ...ificationsActivityIT_empty_light_white.png | Bin ...activity.NotificationsActivityIT_error.png | Bin ...tificationsActivityIT_error_dark_black.png | Bin ...otificationsActivityIT_error_dark_blue.png | Bin ...tificationsActivityIT_error_dark_white.png | Bin ...ificationsActivityIT_error_light_black.png | Bin ...ificationsActivityIT_error_light_white.png | Bin ...ificationsActivityIT_showNotifications.png | Bin ...ctivityIT_showNotifications_dark_black.png | Bin ...ActivityIT_showNotifications_dark_blue.png | Bin ...ctivityIT_showNotifications_dark_white.png | Bin ...tivityIT_showNotifications_light_black.png | Bin ...tivityIT_showNotifications_light_white.png | Bin ...d.ui.activity.PassCodeActivityIT_check.png | Bin ...ty.PassCodeActivityIT_check_dark_black.png | Bin ...ity.PassCodeActivityIT_check_dark_blue.png | Bin ...ty.PassCodeActivityIT_check_dark_white.png | Bin ...y.PassCodeActivityIT_check_light_black.png | Bin ...y.PassCodeActivityIT_check_light_white.png | Bin ....ui.activity.PassCodeActivityIT_delete.png | Bin ...y.PassCodeActivityIT_delete_dark_black.png | Bin ...ty.PassCodeActivityIT_delete_dark_blue.png | Bin ...y.PassCodeActivityIT_delete_dark_white.png | Bin ....PassCodeActivityIT_delete_light_black.png | Bin ....PassCodeActivityIT_delete_light_white.png | Bin ...ui.activity.PassCodeActivityIT_request.png | Bin ....PassCodeActivityIT_request_dark_black.png | Bin ...y.PassCodeActivityIT_request_dark_blue.png | Bin ....PassCodeActivityIT_request_dark_white.png | Bin ...PassCodeActivityIT_request_light_black.png | Bin ...PassCodeActivityIT_request_light_white.png | Bin ...ty.ReceiveExternalFilesActivityIT_open.png | Bin 0 -> 11253 bytes ...ternalFilesActivityIT_openMultiAccount.png | Bin 0 -> 11218 bytes ...dFilesActivityIT_localFolderPickerMode.png | Bin 0 -> 13103 bytes ...ity.UploadFilesActivityIT_noneSelected.png | Bin 0 -> 14696 bytes ...ui.activity.UploadFilesActivityIT_open.png | Bin ....activity.UploadFilesActivityIT_search.png | Bin 0 -> 2941 bytes ...tivity.UploadFilesActivityIT_selectAll.png | Bin 0 -> 7902 bytes ....UserInfoActivityIT_fullUserInfoDetail.png | Bin 0 -> 23869 bytes ...tivityIT_fullUserInfoDetail_dark_black.png | Bin ...ctivityIT_fullUserInfoDetail_dark_blue.png | Bin ...tivityIT_fullUserInfoDetail_dark_white.png | Bin ...ivityIT_fullUserInfoDetail_light_black.png | Bin ...ivityIT_fullUserInfoDetail_light_white.png | Bin ...logFragmentIT_testAccountChooserDialog.png | Bin 0 -> 17356 bytes ...AccountChooserDialogWithStatusDisabled.png | Bin 0 -> 12329 bytes ...serDialogWithStatusDisabled_dark_black.png | Bin ...oserDialogWithStatusDisabled_dark_blue.png | Bin ...serDialogWithStatusDisabled_dark_white.png | Bin ...erDialogWithStatusDisabled_light_black.png | Bin ...erDialogWithStatusDisabled_light_white.png | Bin ...agmentIT_testAccountChooserDialog_away.png | Bin 0 -> 18891 bytes ...stAccountChooserDialog_away_dark_black.png | Bin ...estAccountChooserDialog_away_dark_blue.png | Bin ...stAccountChooserDialog_away_dark_white.png | Bin ...tAccountChooserDialog_away_light_black.png | Bin ...tAccountChooserDialog_away_light_white.png | Bin ...IT_testAccountChooserDialog_dark_black.png | Bin ...tIT_testAccountChooserDialog_dark_blue.png | Bin ...IT_testAccountChooserDialog_dark_white.png | Bin ...ragmentIT_testAccountChooserDialog_dnd.png | Bin 0 -> 19145 bytes ...estAccountChooserDialog_dnd_dark_black.png | Bin ...testAccountChooserDialog_dnd_dark_blue.png | Bin ...estAccountChooserDialog_dnd_dark_white.png | Bin ...stAccountChooserDialog_dnd_light_black.png | Bin ...stAccountChooserDialog_dnd_light_white.png | Bin ...ragmentIT_testAccountChooserDialog_fun.png | Bin 0 -> 19203 bytes ...estAccountChooserDialog_fun_dark_black.png | Bin ...testAccountChooserDialog_fun_dark_blue.png | Bin ...estAccountChooserDialog_fun_dark_white.png | Bin ...stAccountChooserDialog_fun_light_black.png | Bin ...stAccountChooserDialog_fun_light_white.png | Bin ...T_testAccountChooserDialog_light_black.png | Bin ...T_testAccountChooserDialog_light_white.png | Bin ...entIT_testAccountChooserDialog_offline.png | Bin 0 -> 17356 bytes ...ccountChooserDialog_offline_dark_black.png | Bin ...AccountChooserDialog_offline_dark_blue.png | Bin ...ccountChooserDialog_offline_dark_white.png | Bin ...countChooserDialog_offline_light_black.png | Bin ...countChooserDialog_offline_light_white.png | Bin ...mentIT_testAccountChooserDialog_online.png | Bin 0 -> 17475 bytes ...AccountChooserDialog_online_dark_black.png | Bin ...tAccountChooserDialog_online_dark_blue.png | Bin ...AccountChooserDialog_online_dark_white.png | Bin ...ccountChooserDialog_online_light_black.png | Bin ...ccountChooserDialog_online_light_white.png | Bin ...ialog.DialogFragmentIT_testBottomSheet.png | Bin 0 -> 23286 bytes ...gFragmentIT_testBottomSheet_dark_black.png | Bin ...ogFragmentIT_testBottomSheet_dark_blue.png | Bin ...gFragmentIT_testBottomSheet_dark_white.png | Bin ...FragmentIT_testBottomSheet_light_black.png | Bin ...FragmentIT_testBottomSheet_light_white.png | Bin ...IT_testConfirmationDialogWithOneAction.png | Bin 0 -> 5824 bytes ..._testConfirmationDialogWithThreeAction.png | Bin 0 -> 7091 bytes ...stConfirmationDialogWithThreeActionRTL.png | Bin 0 -> 5313 bytes ...IT_testConfirmationDialogWithTwoAction.png | Bin 0 -> 6658 bytes ...gFragmentIT_testEnforcedPasswordDialog.png | Bin 0 -> 7178 bytes ...gFragmentIT_testFileActionsBottomSheet.png | Bin 0 -> 15816 bytes ...log.DialogFragmentIT_testLoadingDialog.png | Bin 0 -> 1458 bytes ...ragmentIT_testLoadingDialog_dark_black.png | Bin ...FragmentIT_testLoadingDialog_dark_blue.png | Bin ...ragmentIT_testLoadingDialog_dark_white.png | Bin ...agmentIT_testLoadingDialog_light_black.png | Bin ...agmentIT_testLoadingDialog_light_white.png | Bin ...g.DialogFragmentIT_testNewFolderDialog.png | Bin 0 -> 6098 bytes ...gmentIT_testNewFolderDialog_dark_black.png | Bin ...agmentIT_testNewFolderDialog_dark_blue.png | Bin ...gmentIT_testNewFolderDialog_dark_white.png | Bin ...mentIT_testNewFolderDialog_light_black.png | Bin ...mentIT_testNewFolderDialog_light_white.png | Bin ...gFragmentIT_testOptionalPasswordDialog.png | Bin 0 -> 7357 bytes ...ialogFragmentIT_testProfileBottomSheet.png | Bin 0 -> 10667 bytes ....DialogFragmentIT_testRemoveFileDialog.png | Bin 0 -> 4782 bytes ...mentIT_testRemoveFileDialog_dark_black.png | Bin ...gmentIT_testRemoveFileDialog_dark_blue.png | Bin ...mentIT_testRemoveFileDialog_dark_white.png | Bin ...entIT_testRemoveFileDialog_light_black.png | Bin ...entIT_testRemoveFileDialog_light_white.png | Bin ...DialogFragmentIT_testRemoveFilesDialog.png | Bin 0 -> 5045 bytes ...entIT_testRemoveFilesDialog_dark_black.png | Bin ...mentIT_testRemoveFilesDialog_dark_blue.png | Bin ...entIT_testRemoveFilesDialog_dark_white.png | Bin ...ntIT_testRemoveFilesDialog_light_black.png | Bin ...ntIT_testRemoveFilesDialog_light_white.png | Bin ...ialogFragmentIT_testRemoveFolderDialog.png | Bin 0 -> 4572 bytes ...ntIT_testRemoveFolderDialog_dark_black.png | Bin ...entIT_testRemoveFolderDialog_dark_blue.png | Bin ...ntIT_testRemoveFolderDialog_dark_white.png | Bin ...tIT_testRemoveFolderDialog_light_black.png | Bin ...tIT_testRemoveFolderDialog_light_white.png | Bin ...alogFragmentIT_testRemoveFoldersDialog.png | Bin 0 -> 5045 bytes ...tIT_testRemoveFoldersDialog_dark_black.png | Bin ...ntIT_testRemoveFoldersDialog_dark_blue.png | Bin ...tIT_testRemoveFoldersDialog_dark_white.png | Bin ...IT_testRemoveFoldersDialog_light_black.png | Bin ...IT_testRemoveFoldersDialog_light_white.png | Bin ....DialogFragmentIT_testRenameFileDialog.png | Bin 0 -> 6885 bytes ...mentIT_testRenameFileDialog_dark_black.png | Bin ...gmentIT_testRenameFileDialog_dark_blue.png | Bin ...mentIT_testRenameFileDialog_dark_white.png | Bin ...entIT_testRenameFileDialog_light_black.png | Bin ...entIT_testRenameFileDialog_light_white.png | Bin ...gFragmentIT_testSslUntrustedCertDialog.png | Bin 0 -> 10592 bytes ...FragmentIT_testStoragePermissionDialog.png | Bin 0 -> 13572 bytes ...gFragmentTest_testAccountChooserDialog.png | Bin ...g.DialogFragmentTest_testLoadingDialog.png | Bin ...DialogFragmentTest_testNewFolderDialog.png | Bin ...ialogFragmentTest_testRemoveFileDialog.png | Bin ...alogFragmentTest_testRemoveFilesDialog.png | Bin ...logFragmentTest_testRemoveFolderDialog.png | Bin ...ogFragmentTest_testRemoveFoldersDialog.png | Bin ...ialogFragmentTest_testRenameFileDialog.png | Bin ...st_showDialogDifferentTypes_Screenshot.png | Bin 0 -> 18283 bytes ...dFilesDialogTest_showDialog_Screenshot.png | Bin 0 -> 18283 bytes ...dFilesDialogTest_showDialog_dark_black.png | Bin ...ndFilesDialogTest_showDialog_dark_blue.png | Bin ...dFilesDialogTest_showDialog_dark_white.png | Bin ...FilesDialogTest_showDialog_light_black.png | Bin ...FilesDialogTest_showDialog_light_white.png | Bin ....dialog.SendShareDialogTest_showDialog.png | Bin 0 -> 17499 bytes ...dShareDialogTest_showDialog_dark_black.png | Bin ...ndShareDialogTest_showDialog_dark_blue.png | Bin ...dShareDialogTest_showDialog_dark_white.png | Bin ...ShareDialogTest_showDialog_light_black.png | Bin ...ShareDialogTest_showDialog_light_white.png | Bin ....SetupEncryptionDialogFragmentIT_error.png | Bin 0 -> 6224 bytes ...ptionDialogFragmentIT_error_dark_black.png | Bin ...yptionDialogFragmentIT_error_dark_blue.png | Bin ...ptionDialogFragmentIT_error_dark_white.png | Bin ...tionDialogFragmentIT_error_light_black.png | Bin ...tionDialogFragmentIT_error_light_white.png | Bin ...ncryptionDialogFragmentIT_showMnemonic.png | Bin 0 -> 16655 bytes ...alogFragmentIT_showMnemonic_dark_black.png | Bin ...ialogFragmentIT_showMnemonic_dark_blue.png | Bin ...alogFragmentIT_showMnemonic_dark_white.png | Bin ...logFragmentIT_showMnemonic_light_black.png | Bin ...logFragmentIT_showMnemonic_light_white.png | Bin ...ntTest_showNotEnoughSpaceDialogForFile.png | Bin ...Test_showNotEnoughSpaceDialogForFolder.png | Bin ...droid.ui.fragment.AvatarIT_showAvatars.png | Bin ...ragment.AvatarIT_showAvatarsWithStatus.png | Bin ...tarIT_showAvatarsWithStatus_dark_black.png | Bin ...atarIT_showAvatarsWithStatus_dark_blue.png | Bin ...tarIT_showAvatarsWithStatus_dark_white.png | Bin ...arIT_showAvatarsWithStatus_light_black.png | Bin ...arIT_showAvatarsWithStatus_light_white.png | Bin ...agment.AvatarIT_showAvatars_dark_black.png | Bin ...ragment.AvatarIT_showAvatars_dark_blue.png | Bin ...agment.AvatarIT_showAvatars_dark_white.png | Bin ...gment.AvatarIT_showAvatars_light_black.png | Bin ...gment.AvatarIT_showAvatars_light_white.png | Bin ...FragmentIT_showCalendarAndContactsList.png | Bin 0 -> 13016 bytes ....BackupListFragmentIT_showCalendarList.png | Bin 0 -> 9679 bytes ...t.BackupListFragmentIT_showContactList.png | Bin 0 -> 9151 bytes ...gment.BackupListFragmentIT_showLoading.png | Bin 0 -> 8113 bytes ...gmentIT_showContactListFragmentLoading.png | Bin ...wContactListFragmentLoading_dark_black.png | Bin ...owContactListFragmentLoading_dark_blue.png | Bin ...wContactListFragmentLoading_dark_white.png | Bin ...ContactListFragmentLoading_light_black.png | Bin ...ContactListFragmentLoading_light_white.png | Bin ...ntStaticServerIT_showDetailsActivities.png | Bin ...ticServerIT_showDetailsActivitiesError.png | Bin ..._showDetailsActivitiesError_dark_black.png | Bin ...T_showDetailsActivitiesError_dark_blue.png | Bin ..._showDetailsActivitiesError_dark_white.png | Bin ...showDetailsActivitiesError_light_black.png | Bin ...showDetailsActivitiesError_light_white.png | Bin ...aticServerIT_showDetailsActivitiesNone.png | Bin ...T_showDetailsActivitiesNone_dark_black.png | Bin ...IT_showDetailsActivitiesNone_dark_blue.png | Bin ...T_showDetailsActivitiesNone_dark_white.png | Bin ..._showDetailsActivitiesNone_light_black.png | Bin ..._showDetailsActivitiesNone_light_white.png | Bin ...verIT_showDetailsActivities_dark_black.png | Bin ...rverIT_showDetailsActivities_dark_blue.png | Bin ...verIT_showDetailsActivities_dark_white.png | Bin ...erIT_showDetailsActivities_light_black.png | Bin ...erIT_showDetailsActivities_light_white.png | Bin ...gmentStaticServerIT_showDetailsSharing.png | Bin ...ServerIT_showDetailsSharing_dark_black.png | Bin ...cServerIT_showDetailsSharing_dark_blue.png | Bin ...ServerIT_showDetailsSharing_dark_white.png | Bin ...erverIT_showDetailsSharing_light_black.png | Bin ...erverIT_showDetailsSharing_light_white.png | Bin ...tStaticServerIT_showDetails_Activities.png | Bin ...mentStaticServerIT_showDetails_Sharing.png | Bin ...verIT_showFileDetailActivitiesFragment.png | Bin ...ileDetailActivitiesFragment_dark_black.png | Bin ...FileDetailActivitiesFragment_dark_blue.png | Bin ...ileDetailActivitiesFragment_dark_white.png | Bin ...leDetailActivitiesFragment_light_black.png | Bin ...leDetailActivitiesFragment_light_white.png | Bin ...ServerIT_showFileDetailDetailsFragment.png | Bin ...ServerIT_showFileDetailSharingFragment.png | Bin ...owFileDetailSharingFragment_dark_black.png | Bin ...howFileDetailSharingFragment_dark_blue.png | Bin ...owFileDetailSharingFragment_dark_white.png | Bin ...wFileDetailSharingFragment_light_black.png | Bin ...wFileDetailSharingFragment_light_white.png | Bin ...ringFragmentIT_listSharesDownloadLimit.png | Bin 0 -> 22749 bytes ...FragmentIT_listSharesFileAllShareTypes.png | Bin 0 -> 27548 bytes ...listSharesFileAllShareTypes_dark_black.png | Bin ..._listSharesFileAllShareTypes_dark_blue.png | Bin ...listSharesFileAllShareTypes_dark_white.png | Bin ...istSharesFileAllShareTypes_light_black.png | Bin ...istSharesFileAllShareTypes_light_white.png | Bin ...ilSharingFragmentIT_listSharesFileNone.png | Bin 0 -> 14782 bytes ...agmentIT_listSharesFileNone_dark_black.png | Bin ...ragmentIT_listSharesFileNone_dark_blue.png | Bin ...agmentIT_listSharesFileNone_dark_white.png | Bin ...gmentIT_listSharesFileNone_light_black.png | Bin ...gmentIT_listSharesFileNone_light_white.png | Bin ...ntIT_listSharesFileResharingNotAllowed.png | Bin 0 -> 12088 bytes ...aresFileResharingNotAllowed_dark_black.png | Bin ...haresFileResharingNotAllowed_dark_blue.png | Bin ...aresFileResharingNotAllowed_dark_white.png | Bin ...resFileResharingNotAllowed_light_black.png | Bin ...resFileResharingNotAllowed_light_white.png | Bin ...agmentIT_listShares_file_allShareTypes.png | Bin ...SharingFragmentIT_listShares_file_none.png | Bin ..._listShares_file_resharing_not_allowed.png | Bin ...haringFragmentIT_publicLink_optionMenu.png | Bin ...i.fragment.GalleryFragmentIT_showEmpty.png | Bin ...fragment.GalleryFragmentIT_showGallery.png | Bin 0 -> 50976 bytes ...nt.GroupfolderListFragmentIT_showEmpty.png | Bin ...upfolderListFragmentIT_showGroupfolder.png | Bin 0 -> 4964 bytes ...pfolderListFragmentIT_showGroupfolders.png | Bin 0 -> 5897 bytes ...tFragmentIT_createAndShowShareToCircle.png | Bin ...stFragmentIT_createAndShowShareToGroup.png | Bin ...istFragmentIT_createAndShowShareToUser.png | Bin ...stFragmentIT_createAndShowShareViaLink.png | Bin ...leListFragmentStaticServerIT_showFiles.png | Bin 0 -> 14943 bytes ...entStaticServerIT_showFiles_dark_black.png | Bin ...mentStaticServerIT_showFiles_dark_blue.png | Bin ...entStaticServerIT_showFiles_dark_white.png | Bin ...ntStaticServerIT_showFiles_light_black.png | Bin ...ntStaticServerIT_showFiles_light_white.png | Bin ...FragmentStaticServerIT_showFolderTypes.png | Bin 0 -> 21147 bytes ...ListFragmentStaticServerIT_showOneFile.png | Bin ...agmentStaticServerIT_showRichWorkspace.png | Bin 0 -> 27820 bytes ...cServerIT_showRichWorkspace_dark_black.png | Bin ...icServerIT_showRichWorkspace_dark_blue.png | Bin ...cServerIT_showRichWorkspace_dark_white.png | Bin ...ServerIT_showRichWorkspace_light_black.png | Bin ...ServerIT_showRichWorkspace_light_white.png | Bin ...FragmentStaticServerIT_showSharedFiles.png | Bin 0 -> 38893 bytes ...ticServerIT_showSharedFiles_dark_black.png | Bin ...aticServerIT_showSharedFiles_dark_blue.png | Bin ...ticServerIT_showSharedFiles_dark_white.png | Bin ...icServerIT_showSharedFiles_light_black.png | Bin ...icServerIT_showSharedFiles_light_white.png | Bin ...t.SharedListFragmentIT_showSharedFiles.png | Bin 0 -> 20476 bytes ...w.PreviewBitmapScreenshotIT_showBitmap.png | Bin ...ew.PreviewImageFragmentIT_corruptImage.png | Bin ...ImageFragmentIT_corruptImage_dark_blue.png | Bin ...view.PreviewImageFragmentIT_validImage.png | Bin ...ewImageFragmentIT_validImage_dark_blue.png | Bin ...ileFragmentTest_displayJavaSnippetFile.png | Bin 0 -> 31328 bytes ...FileFragmentTest_displaySimpleTextFile.png | Bin 0 -> 15707 bytes ...PreviewPdfFragmentScreenshotIT_showPdf.png | Bin ...shbin.TrashbinActivityIT_differentUser.png | Bin ...d.ui.trashbin.TrashbinActivityIT_empty.png | Bin ...in.TrashbinActivityIT_empty_dark_black.png | Bin ...bin.TrashbinActivityIT_empty_dark_blue.png | Bin ...in.TrashbinActivityIT_empty_dark_white.png | Bin ...n.TrashbinActivityIT_empty_light_black.png | Bin ...n.TrashbinActivityIT_empty_light_white.png | Bin ...d.ui.trashbin.TrashbinActivityIT_error.png | Bin ...in.TrashbinActivityIT_error_dark_black.png | Bin ...bin.TrashbinActivityIT_error_dark_blue.png | Bin ...in.TrashbinActivityIT_error_dark_white.png | Bin ...n.TrashbinActivityIT_error_light_black.png | Bin ...n.TrashbinActivityIT_error_light_white.png | Bin ...d.ui.trashbin.TrashbinActivityIT_files.png | Bin ...in.TrashbinActivityIT_files_dark_black.png | Bin ...bin.TrashbinActivityIT_files_dark_blue.png | Bin ...in.TrashbinActivityIT_files_dark_white.png | Bin ...n.TrashbinActivityIT_files_light_black.png | Bin ...n.TrashbinActivityIT_files_light_white.png | Bin ...ui.trashbin.TrashbinActivityIT_loading.png | Bin ....TrashbinActivityIT_loading_dark_black.png | Bin ...n.TrashbinActivityIT_loading_dark_blue.png | Bin ....TrashbinActivityIT_loading_dark_white.png | Bin ...TrashbinActivityIT_loading_light_black.png | Bin ...TrashbinActivityIT_loading_light_white.png | Bin ...trashbin.TrashbinActivityIT_normalUser.png | Bin .../debug/richworkspaces_dark.png | Bin .../debug/richworkspaces_light.png | Bin ...tcloud.client.CommunityActivityIT_open.png | Bin 76634 -> 0 bytes ...FileDisplayActivityScreenshotIT_drawer.png | Bin 30437 -> 0 bytes ...xtcloud.client.SettingsActivityIT_open.png | Bin 42751 -> 0 bytes ....SettingsActivityIT_showMnemonic_Error.png | Bin 44315 -> 0 bytes ...ud.client.SyncedFoldersActivityIT_open.png | Bin 5756 -> 0 bytes ...ldersActivityIT_testSyncedFolderDialog.png | Bin 41355 -> 0 bytes ...ploadListActivityActivityIT_openDrawer.png | Bin 30039 -> 0 bytes ...ud.client.etm.EtmActivityTest_accounts.png | Bin 14108 -> 0 bytes ...ud.client.etm.EtmActivityTest_overview.png | Bin 19113 -> 0 bytes ...ty.ConflictsResolveActivityIT_keepBoth.png | Bin 16619 -> 0 bytes ...onflictsResolveActivityIT_keepExisting.png | Bin 16669 -> 0 bytes ...ity.ConflictsResolveActivityIT_keepNew.png | Bin 16659 -> 0 bytes ...sResolveActivityIT_screenshotTextFiles.png | Bin 16457 -> 0 bytes ...renceActivityIT_openContactsPreference.png | Bin 19780 -> 0 bytes ...y.ContactsPreferenceActivityIT_openVCF.png | Bin 9341 -> 0 bytes ...i.activity.FolderPickerActivityIT_open.png | Bin 17343 -> 0 bytes ...kerActivityIT_testChooseLocationAction.png | Bin 17484 -> 0 bytes ....FolderPickerActivityIT_testMoveOrCopy.png | Bin 18100 -> 0 bytes ...activity.ManageAccountsActivityIT_open.png | Bin 13600 -> 0 bytes ...anageAccountsActivityIT_userInfoDetail.png | Bin 30888 -> 0 bytes ...ty.ReceiveExternalFilesActivityIT_open.png | Bin 13963 -> 0 bytes ...ternalFilesActivityIT_openMultiAccount.png | Bin 13912 -> 0 bytes ...dFilesActivityIT_localFolderPickerMode.png | Bin 16433 -> 0 bytes ...ity.UploadFilesActivityIT_noneSelected.png | Bin 13562 -> 0 bytes ....UserInfoActivityIT_fullUserInfoDetail.png | Bin 30471 -> 0 bytes ...logFragmentIT_testAccountChooserDialog.png | Bin 23069 -> 0 bytes ...AccountChooserDialogWithStatusDisabled.png | Bin 16428 -> 0 bytes ...agmentIT_testAccountChooserDialog_away.png | Bin 24945 -> 0 bytes ...ragmentIT_testAccountChooserDialog_dnd.png | Bin 25428 -> 0 bytes ...ragmentIT_testAccountChooserDialog_fun.png | Bin 25541 -> 0 bytes ...entIT_testAccountChooserDialog_offline.png | Bin 23069 -> 0 bytes ...mentIT_testAccountChooserDialog_online.png | Bin 23275 -> 0 bytes ...ialog.DialogFragmentIT_testBottomSheet.png | Bin 22763 -> 0 bytes ...IT_testConfirmationDialogWithOneAction.png | Bin 7576 -> 0 bytes ..._testConfirmationDialogWithThreeAction.png | Bin 9223 -> 0 bytes ...stConfirmationDialogWithThreeActionRTL.png | Bin 6977 -> 0 bytes ...IT_testConfirmationDialogWithTwoAction.png | Bin 8637 -> 0 bytes ...gFragmentIT_testEnforcedPasswordDialog.png | Bin 9388 -> 0 bytes ...gFragmentIT_testFileActionsBottomSheet.png | Bin 20521 -> 0 bytes ...log.DialogFragmentIT_testLoadingDialog.png | Bin 1988 -> 0 bytes ...g.DialogFragmentIT_testNewFolderDialog.png | Bin 7827 -> 0 bytes ...gFragmentIT_testOptionalPasswordDialog.png | Bin 9506 -> 0 bytes ...ialogFragmentIT_testProfileBottomSheet.png | Bin 14665 -> 0 bytes ....DialogFragmentIT_testRemoveFileDialog.png | Bin 4818 -> 0 bytes ...DialogFragmentIT_testRemoveFilesDialog.png | Bin 5083 -> 0 bytes ...ialogFragmentIT_testRemoveFolderDialog.png | Bin 4609 -> 0 bytes ...alogFragmentIT_testRemoveFoldersDialog.png | Bin 5083 -> 0 bytes ....DialogFragmentIT_testRenameFileDialog.png | Bin 8644 -> 0 bytes ...gFragmentIT_testSslUntrustedCertDialog.png | Bin 13576 -> 0 bytes ...FragmentIT_testStoragePermissionDialog.png | Bin 18812 -> 0 bytes ...st_showDialogDifferentTypes_Screenshot.png | Bin 21339 -> 0 bytes ...dFilesDialogTest_showDialog_Screenshot.png | Bin 21339 -> 0 bytes ....dialog.SendShareDialogTest_showDialog.png | Bin 24764 -> 0 bytes ....SetupEncryptionDialogFragmentIT_error.png | Bin 7932 -> 0 bytes ...ncryptionDialogFragmentIT_showMnemonic.png | Bin 21829 -> 0 bytes ...FragmentIT_showCalendarAndContactsList.png | Bin 16898 -> 0 bytes ....BackupListFragmentIT_showCalendarList.png | Bin 12104 -> 0 bytes ...t.BackupListFragmentIT_showContactList.png | Bin 11910 -> 0 bytes ...gment.BackupListFragmentIT_showLoading.png | Bin 9341 -> 0 bytes ...FragmentIT_listSharesFileAllShareTypes.png | Bin 41653 -> 0 bytes ...ilSharingFragmentIT_listSharesFileNone.png | Bin 15673 -> 0 bytes ...ntIT_listSharesFileResharingNotAllowed.png | Bin 14900 -> 0 bytes ...fragment.GalleryFragmentIT_showGallery.png | Bin 43125 -> 0 bytes ...upfolderListFragmentIT_showGroupfolder.png | Bin 5469 -> 0 bytes ...pfolderListFragmentIT_showGroupfolders.png | Bin 6921 -> 0 bytes ...leListFragmentStaticServerIT_showFiles.png | Bin 14664 -> 0 bytes ...FragmentStaticServerIT_showFolderTypes.png | Bin 17970 -> 0 bytes ...agmentStaticServerIT_showRichWorkspace.png | Bin 39848 -> 0 bytes ...FragmentStaticServerIT_showSharedFiles.png | Bin 49642 -> 0 bytes ...t.SharedListFragmentIT_showSharedFiles.png | Bin 25586 -> 0 bytes ...ileFragmentTest_displayJavaSnippetFile.png | Bin 31094 -> 0 bytes ...FileFragmentTest_displaySimpleTextFile.png | Bin 15155 -> 0 bytes app/src/androidTest/AndroidManifest.xml | 2 +- app/src/androidTest/assets/credentials.json | 4 + .../uiautomator/InitialTest.java | 2 +- .../nextcloud/client/ActivitiesActivityIT.kt | 2 +- .../client/AuthenticatorActivityIT.java | 2 +- .../nextcloud/client/CommunityActivityIT.java | 39 - .../nextcloud/client/CommunityActivityIT.kt | 59 + .../com/nextcloud/client/EndToEndAction.java | 2 +- .../nextcloud/client/FileDisplayActivityIT.kt | 260 +- .../client/FileDisplayActivityScreenshotIT.kt | 153 +- .../nextcloud/client/FirstRunActivityIT.java | 34 - .../nextcloud/client/FirstRunActivityIT.kt | 53 + .../nextcloud/client/SettingsActivityIT.kt | 92 +- .../client/SyncedFoldersActivityIT.java | 81 - .../client/SyncedFoldersActivityIT.kt | 132 + .../java/com/nextcloud/client/TestRunner.kt | 2 +- .../client/UploadListActivityActivityIT.java | 30 - .../client/UploadListActivityActivityIT.kt | 58 + .../client/account/AnonymousUserTest.kt | 2 +- .../nextcloud/client/account/MockUserTest.kt | 2 +- .../account/OwnCloudClientManagerTest.java | 2 +- .../client/account/RegisteredUserTest.kt | 6 +- .../account/UserAccountManagerImplTest.java | 2 +- .../assistant/AssistantRepositoryTests.kt | 33 +- .../database/migrations/MigrationTest.kt | 2 +- .../documentscan/GeneratePDFUseCaseTest.kt | 2 +- .../nextcloud/client/etm/EtmActivityTest.kt | 61 +- .../client/files/DeepLinkHandlerTest.kt | 16 +- .../files/download/DownloaderServiceTest.kt | 2 +- .../client/files/download/RegistryTest.kt | 2 +- .../download/TransferManagerConnectionTest.kt | 2 +- .../files/download/TransferManagerTest.kt | 2 +- .../client/integrations/deck/DeckApiTest.kt | 6 +- .../client/jobs/BackgroundJobManagerTest.kt | 35 +- .../nextcloud/client/jobs/ContactsBackupIT.kt | 111 +- .../client/migrations/MigrationsDbTest.kt | 2 +- .../migrations/MigrationsManagerTest.kt | 2 +- .../migrations/MockSharedPreferences.kt | 23 +- .../migrations/MockSharedPreferencesTest.kt | 2 +- .../network/ConnectivityServiceImplIT.kt | 2 +- .../extensions/BitmapRotationTests.kt | 90 + .../extensions/BundleExtensionTests.kt | 4 +- .../extensions/GetExifOrientationTests.kt | 78 + .../extensions/IntentExtensionTests.kt | 4 +- .../extensions/StringExtensionTests.kt | 176 + .../nextcloud/sso/InputStreamBinderTest.kt | 2 +- .../test/GrantStoragePermissionRule.kt | 2 +- .../nextcloud/test/InjectionOverrideRule.kt | 2 +- .../test/InjectionTestActivityTest.kt | 2 +- .../nextcloud/test/RandomStringGenerator.kt | 10 +- .../java/com/nextcloud/test/RetryTestRule.kt | 6 +- .../java/com/nextcloud/test/TestMainApp.kt | 2 +- .../com/nextcloud/test/model/TestModels.kt | 16 +- .../java/com/nextcloud/ui/BitmapIT.kt | 158 +- .../ui/SetOnlineStatusBottomSheetIT.kt | 56 + .../nextcloud/ui/SetStatusDialogFragmentIT.kt | 45 - .../ui/SetStatusMessageBottomSheetIT.kt | 68 + .../com/nextcloud/utils/AutoRenameTests.kt | 253 + .../utils/CertificateValidatorTests.kt | 45 + .../utils/CheckWCFRestrictionsTests.kt | 63 + .../com/nextcloud/utils/FileHelperTest.kt | 204 + .../nextcloud/utils/FileNameValidatorTests.kt | 243 + .../utils/SharePermissionManagerTest.kt | 272 + .../com/nmc/android/ui/LauncherActivityIT.kt | 69 +- .../java/com/owncloud/android/AbstractIT.java | 82 +- .../owncloud/android/AbstractOnServerIT.java | 12 +- .../java/com/owncloud/android/DownloadIT.java | 2 +- .../java/com/owncloud/android/EncryptionIT.kt | 4 +- .../java/com/owncloud/android/FileIT.java | 6 +- .../com/owncloud/android/ScreenshotsIT.java | 137 - .../com/owncloud/android/ScreenshotsIT.kt | 183 + .../java/com/owncloud/android/UploadIT.java | 21 +- .../authentication/AuthenticatorActivityIT.kt | 2 +- .../authentication/PassCodeManagerIT.kt | 2 +- .../datamodel/ArbitraryDataProviderIT.kt | 2 +- .../datamodel/ContentResolverHelperIT.kt | 3 +- .../owncloud/android/datamodel/Credentials.kt | 10 + ...StorageManagerContentProviderClientIT.java | 2 +- ...FileDataStorageManagerContentResolverIT.kt | 2 +- .../datamodel/FileDataStorageManagerIT.java | 2 +- .../android/datamodel/OCCapabilityIT.kt | 2 +- .../android/datamodel/OCFileIconTests.kt | 4 +- .../android/datamodel/OCFileUnitTest.java | 3 + .../datamodel/UploadStorageManagerTest.java | 2 +- .../extensions/AbstractITExtensions.kt | 39 + .../android/files/FileMenuFilterIT.kt | 9 +- .../android/files/services/FileUploaderIT.kt | 8 +- .../files/services/LegacyFileUploaderIT.kt | 10 - .../operations/GetSharesForFileOperationIT.kt | 2 +- .../operations/RemoveFileOperationIT.java | 2 +- .../providers/DocumentsProviderUtils.kt | 47 +- .../providers/DocumentsStorageProviderIT.kt | 10 +- .../FileContentProviderVerificationIT.kt | 2 +- .../UsersAndGroupsSearchProviderIT.kt | 30 +- .../java/com/owncloud/android/ui/LoginIT.kt | 13 +- .../activity/ConflictsResolveActivityIT.java | 318 - .../ui/activity/ConflictsResolveActivityIT.kt | 326 + .../activity/ContactsPreferenceActivityIT.kt | 63 +- .../android/ui/activity/DrawerActivityIT.java | 112 - .../android/ui/activity/DrawerActivityIT.kt | 128 + .../ui/activity/FileDisplayActivityTest.java | 2 +- .../ui/activity/FolderPickerActivityIT.java | 147 - .../ui/activity/FolderPickerActivityIT.kt | 213 + .../ui/activity/ManageAccountsActivityIT.java | 66 - .../ui/activity/ManageAccountsActivityIT.kt | 89 + .../ui/activity/NotificationsActivityIT.kt | 2 +- .../android/ui/activity/PassCodeActivityIT.kt | 103 +- .../ReceiveExternalFilesActivityIT.kt | 41 +- .../ui/activity/UploadFilesActivityIT.kt | 165 +- .../ui/activity/UserInfoActivityIT.java | 50 - .../android/ui/activity/UserInfoActivityIT.kt | 71 + .../android/ui/adapter/OCFileListAdapterIT.kt | 2 +- .../android/ui/dialog/DialogFragmentIT.java | 630 - .../android/ui/dialog/DialogFragmentIT.kt | 777 + .../android/ui/dialog/SendFilesDialogTest.kt | 91 +- .../android/ui/dialog/SendShareDialogTest.kt | 49 +- .../dialog/SetupEncryptionDialogFragmentIT.kt | 114 +- ...cFileNotEnoughSpaceDialogFragmentTest.java | 59 - ...yncFileNotEnoughSpaceDialogFragmentTest.kt | 89 + .../owncloud/android/ui/fragment/AvatarIT.kt | 284 +- .../android/ui/fragment/AvatarTestFragment.kt | 12 +- .../ui/fragment/BackupListFragmentIT.kt | 156 +- .../FileDetailFragmentStaticServerIT.kt | 230 +- .../fragment/FileDetailSharingFragmentIT.kt | 1288 +- .../android/ui/fragment/GalleryFragmentIT.kt | 79 +- .../ui/fragment/GroupfolderListFragmentIT.kt | 100 +- .../OCFileListFragmentStaticServerIT.kt | 651 +- .../ui/fragment/SharedListFragmentIT.kt | 273 +- .../fragment/UnifiedSearchFakeRepository.kt | 2 +- .../ui/fragment/UnifiedSearchFragmentIT.kt | 139 +- .../ui/helpers/FileOperationsHelperIT.kt | 2 +- .../android/ui/helpers/UriUploaderIT.kt | 2 +- .../ui/preview/PreviewBitmapScreenshotIT.kt | 50 +- .../ui/preview/PreviewImageFragmentIT.kt | 60 - .../preview/PreviewTextFileFragmentTest.java | 64 - .../ui/preview/PreviewTextFileFragmentTest.kt | 89 + .../pdf/PreviewPdfFragmentScreenshotIT.kt | 68 +- .../android/ui/trashbin/TrashbinActivityIT.kt | 112 +- .../ui/trashbin/TrashbinLocalRepository.kt | 20 +- .../android/util/EncryptionTestIT.java | 21 +- .../android/util/ErrorMessageAdapterIT.java | 2 +- .../owncloud/android/utils/BitmapUtilsIT.kt | 2 +- .../owncloud/android/utils/DisplayUtilsIT.kt | 2 +- .../android/utils/DrawableUtilTests.kt | 16 +- .../android/utils/EncryptionTestUtils.kt | 4 +- .../android/utils/EncryptionUtilsIT.kt | 2 +- .../android/utils/EncryptionUtilsV2IT.kt | 52 +- .../android/utils/EspressoIdlingResource.kt | 2 +- .../android/utils/FileExportUtilsIT.kt | 2 +- .../android/utils/FileStorageUtilsIT.kt | 7 +- .../owncloud/android/utils/FileUtilTest.kt | 2 +- .../android/utils/SessionMixinTest.kt | 2 +- .../android/utils/SyncedFolderUtilsTest.kt | 18 +- .../android/utils/theme/CapabilityUtilsIT.kt | 24 +- app/src/debug/AndroidManifest.xml | 2 +- .../client/di/BuildTypeComponentsModule.kt | 2 +- .../nextcloud/test/InjectionTestActivity.kt | 6 +- .../java/com/nextcloud/test/TestActivity.kt | 28 +- .../res/layout/activity_injection_test.xml | 2 +- app/src/debug/res/layout/avatar_fragment.xml | 2 +- .../appReview/InAppReviewHelperImpl.kt | 5 +- .../client/di/VariantComponentsModule.java | 2 +- .../com/nextcloud/client/di/VariantModule.kt | 6 +- .../com/owncloud/android/utils/PushUtils.java | 2 +- .../owncloud/android/utils/SecurityUtils.java | 2 +- app/src/gplay/AndroidManifest.xml | 4 +- .../appReview/InAppReviewHelperImpl.kt | 8 +- .../client/di/VariantComponentsModule.java | 2 +- .../com/nextcloud/client/di/VariantModule.kt | 8 +- .../ModifiedAuthenticatorActivity.java | 2 +- .../firebase/NCFirebaseMessagingService.java | 47 +- .../owncloud/android/utils/GooglePlayUtils.kt | 2 +- .../com/owncloud/android/utils/PushUtils.java | 20 +- .../owncloud/android/utils/SecurityUtils.java | 2 +- app/src/gplay/res/values/setup.xml | 2 +- app/src/huawei/AndroidManifest.xml | 2 +- .../appReview/InAppReviewHelperImpl.kt | 5 +- .../client/di/VariantComponentsModule.java | 2 +- .../com/nextcloud/client/di/VariantModule.kt | 8 +- .../ui/activity/HuaweiCommunityActivity.kt | 2 +- .../com/owncloud/android/utils/PushUtils.java | 2 +- .../owncloud/android/utils/SecurityUtils.java | 2 +- app/src/huawei/res/values/bools.xml | 2 +- app/src/main/AndroidManifest.xml | 129 +- .../android/files/FileLockingHelper.kt | 2 +- .../com/nextcloud/android/sso/Constants.java | 2 +- .../android/sso/InputStreamBinder.java | 17 +- .../nextcloud/android/sso/PatchMethod.java | 2 +- .../nextcloud/android/sso/PlainHeader.java | 2 +- .../com/nextcloud/android/sso/QueryParam.java | 2 +- .../com/nextcloud/android/sso/Response.java | 2 +- .../android/sso/aidl/IThreadListener.java | 2 +- .../android/sso/aidl/NextcloudRequest.java | 2 +- .../sso/aidl/ParcelFileDescriptorUtil.java | 2 +- .../appReview/AppReviewShownModel.kt | 2 +- .../nextcloud/appReview/InAppReviewHelper.kt | 2 +- .../nextcloud/appReview/InAppReviewModule.kt | 7 +- .../com/nextcloud/client/NominatimClient.kt | 4 +- .../nextcloud/client/account/AnonymousUser.kt | 23 +- .../account/CurrentAccountProvider.java | 3 +- .../com/nextcloud/client/account/MockUser.kt | 23 +- .../client/account/RegisteredUser.kt | 19 +- .../com/nextcloud/client/account/Server.kt | 2 +- .../java/com/nextcloud/client/account/User.kt | 6 +- .../client/account/UserAccountManager.java | 2 +- .../account/UserAccountManagerImpl.java | 85 +- .../com/nextcloud/client/appinfo/AppInfo.kt | 2 +- .../nextcloud/client/appinfo/AppInfoImpl.kt | 20 +- .../nextcloud/client/appinfo/AppInfoModule.kt | 6 +- .../client/assistant/AssistantViewModel.kt | 156 +- .../client/assistant/AsssistantScreen.kt | 352 +- .../assistant/component/AddTaskAlertDialog.kt | 16 +- .../client/assistant/component/CenterText.kt | 9 +- .../assistant/extensions/TaskExtensions.kt | 140 +- .../assistant/model/ScreenOverlayState.kt | 79 + .../client/assistant/model/ScreenState.kt | 14 + .../repository/AssistantMockRepository.kt | 156 +- .../repository/AssistantRepository.kt | 81 +- .../repository/AssistantRepositoryType.kt | 16 +- .../client/assistant/task/TaskStatus.kt | 58 - .../client/assistant/task/TaskStatusView.kt | 151 + .../client/assistant/task/TaskView.kt | 184 +- .../taskDetail/TaskDetailBottomSheet.kt | 161 +- .../assistant/taskTypes/TaskTypesRow.kt | 81 +- .../com/nextcloud/client/core/AsyncRunner.kt | 2 +- .../com/nextcloud/client/core/Cancellable.kt | 2 +- .../java/com/nextcloud/client/core/Clock.kt | 2 +- .../com/nextcloud/client/core/ClockImpl.kt | 2 +- .../com/nextcloud/client/core/LocalBinder.kt | 2 +- .../nextcloud/client/core/LocalConnection.kt | 10 +- .../client/core/ManualAsyncRunner.kt | 16 +- .../java/com/nextcloud/client/core/Task.kt | 5 +- .../client/core/ThreadPoolAsyncRunner.kt | 2 +- .../client/database/DatabaseModule.kt | 19 +- .../client/database/NextcloudDatabase.kt | 49 +- .../client/database/dao/ArbitraryDataDao.kt | 2 +- .../nextcloud/client/database/dao/FileDao.kt | 61 +- .../database/dao/OfflineOperationDao.kt | 46 + .../client/database/dao/RecommendedFileDao.kt | 26 + .../client/database/dao/UploadDao.kt | 30 + .../database/entity/ArbitraryDataEntity.kt | 2 +- .../database/entity/CapabilityEntity.kt | 28 +- .../database/entity/ExternalLinkEntity.kt | 2 +- .../client/database/entity/FileEntity.kt | 14 +- .../database/entity/FilesystemEntity.kt | 2 +- .../database/entity/OfflineOperationEntity.kt | 68 + .../database/entity/RecommendedFileEntity.kt | 72 + .../client/database/entity/ShareEntity.kt | 10 +- .../database/entity/SyncedFolderEntity.kt | 2 +- .../client/database/entity/UploadEntity.kt | 32 +- .../client/database/entity/VirtualEntity.kt | 2 +- .../migrations/DatabaseMigrationUtil.kt | 50 +- .../database/migrations/LegacyMigration.kt | 2 +- .../migrations/LegacyMigrationHelper.java | 2 +- .../database/migrations/Migration67to68.kt | 2 +- .../database/migrations/Migration88to89.kt | 31 + .../database/migrations/RoomMigration.kt | 2 +- .../migrations/model/SQLiteColumnType.kt | 13 + .../OfflineOperationTypeAdapter.kt | 96 + .../OfflineOperationTypeConverter.kt | 30 + .../nextcloud/client/device/BatteryStatus.kt | 2 +- .../com/nextcloud/client/device/DeviceInfo.kt | 9 +- .../nextcloud/client/device/DeviceModule.kt | 4 +- .../client/device/PowerManagementService.kt | 2 +- .../device/PowerManagementServiceImpl.kt | 2 +- .../nextcloud/client/di/ActivityInjector.kt | 2 +- .../com/nextcloud/client/di/AppComponent.java | 16 +- .../com/nextcloud/client/di/AppModule.java | 15 +- .../nextcloud/client/di/ComponentsModule.java | 45 +- .../nextcloud/client/di/DispatcherModule.kt | 2 +- .../nextcloud/client/di/FragmentInjector.kt | 8 +- .../com/nextcloud/client/di/Injectable.java | 2 +- .../client/di/InjectorNotFoundException.java | 2 +- .../com/nextcloud/client/di/ThemeModule.kt | 15 +- .../nextcloud/client/di/ViewModelFactory.kt | 2 +- .../com/nextcloud/client/di/ViewModelKey.kt | 2 +- .../nextcloud/client/di/ViewModelModule.kt | 9 +- .../com/nextcloud/client/di/package-info.java | 2 +- .../documentscan/AppScanOptionalFeature.kt | 5 +- .../documentscan/DocumentPageListAdapter.kt | 12 +- .../documentscan/DocumentScanActivity.kt | 26 +- .../documentscan/DocumentScanViewModel.kt | 14 +- .../client/documentscan/GeneratePDFUseCase.kt | 46 +- .../documentscan/GeneratePdfFromImagesWork.kt | 5 +- .../client/editimage/EditImageActivity.kt | 12 +- .../client/errorhandling/ExceptionHandler.kt | 2 +- .../client/errorhandling/ShowErrorActivity.kt | 20 +- .../com/nextcloud/client/etm/EtmActivity.kt | 20 +- .../nextcloud/client/etm/EtmBaseFragment.kt | 2 +- .../nextcloud/client/etm/EtmMenuAdapter.kt | 12 +- .../com/nextcloud/client/etm/EtmMenuEntry.kt | 2 +- .../nextcloud/client/etm/EtmMenuFragment.kt | 2 +- .../com/nextcloud/client/etm/EtmViewModel.kt | 4 +- .../client/etm/pages/EtmAccountsFragment.kt | 16 +- .../etm/pages/EtmBackgroundJobsFragment.kt | 76 +- .../etm/pages/EtmFileTransferFragment.kt | 34 +- .../client/etm/pages/EtmMigrations.kt | 18 +- .../etm/pages/EtmPreferencesFragment.kt | 16 +- .../client/files/DeepLinkConstants.kt | 31 + .../nextcloud/client/files/DeepLinkHandler.kt | 12 +- .../com/nextcloud/client/files/Direction.kt | 2 +- .../com/nextcloud/client/files/Registry.kt | 8 +- .../com/nextcloud/client/files/Request.kt | 37 +- .../client/integrations/IntegrationsModule.kt | 6 +- .../client/integrations/deck/DeckApi.kt | 5 +- .../client/integrations/deck/DeckApiImpl.kt | 22 +- .../client/jobs/AccountRemovalWork.kt | 2 +- .../client/jobs/BackgroundJobFactory.kt | 176 +- .../client/jobs/BackgroundJobManager.kt | 24 +- .../client/jobs/BackgroundJobManagerImpl.kt | 312 +- .../client/jobs/CalendarBackupWork.kt | 2 +- .../client/jobs/CalendarImportWork.kt | 63 +- .../client/jobs/ContactsBackupWork.kt | 2 +- .../client/jobs/ContactsImportWork.kt | 54 +- .../client/jobs/ContentObserverWork.kt | 21 +- .../nextcloud/client/jobs/FilesExportWork.kt | 18 +- .../nextcloud/client/jobs/FilesSyncWork.kt | 294 +- .../nextcloud/client/jobs/HealthStatusWork.kt | 2 +- .../client/jobs/InternalTwoWaySyncWork.kt | 136 + .../java/com/nextcloud/client/jobs/JobInfo.kt | 2 +- .../com/nextcloud/client/jobs/JobsModule.kt | 10 +- .../client/jobs/MediaFoldersDetectionWork.kt | 9 +- .../nextcloud/client/jobs/NotificationWork.kt | 25 +- .../nextcloud/client/jobs/OfflineSyncWork.kt | 34 +- .../java/com/nextcloud/client/jobs/TestJob.kt | 9 +- .../jobs/clipboard/ClipboardClearWorker.kt | 49 + .../download/DownloadNotificationManager.kt | 114 +- .../client/jobs/download/DownloadTask.kt | 6 +- .../client/jobs/download/FileDownloadError.kt | 7 +- .../jobs/download/FileDownloadHelper.kt | 28 +- .../jobs/download/FileDownloadIntents.kt | 76 +- .../jobs/download/FileDownloadWorker.kt | 146 +- .../client/jobs/metadata/MetadataWorker.kt | 83 + .../notification/WorkerNotificationManager.kt | 70 + .../OfflineOperationsNotificationManager.kt | 168 + .../OfflineOperationsWorker.kt | 301 + .../receiver/OfflineOperationReceiver.kt | 41 + .../repository/OfflineOperationsRepository.kt | 114 + .../OfflineOperationsRepositoryType.kt | 18 + .../jobs/operation/FileOperationHelper.kt | 66 + .../jobs/transfer/FileTransferService.kt | 20 +- .../client/jobs/transfer/Transfer.kt | 4 +- .../client/jobs/transfer/TransferManager.kt | 10 +- .../transfer/TransferManagerConnection.kt | 15 +- .../jobs/transfer/TransferManagerImpl.kt | 39 +- .../client/jobs/transfer/TransferState.kt | 2 +- .../upload/FileUploadBroadcastReceiver.kt | 48 + .../client/jobs/upload/FileUploadHelper.kt | 290 +- .../client/jobs/upload/FileUploadWorker.kt | 302 +- .../jobs/upload/FileUploaderDelegate.kt | 4 +- .../client/jobs/upload/FileUploaderIntents.kt | 46 +- .../client/jobs/upload/PostUploadAction.kt | 2 +- .../jobs/upload/UploadNotificationManager.kt | 150 +- .../client/jobs/upload/UploadTask.kt | 22 +- .../client/jobs/upload/UploadTrigger.kt | 4 +- .../nextcloud/client/logger/FileLogHandler.kt | 2 +- .../client/logger/LegacyLoggerAdapter.kt | 2 +- .../java/com/nextcloud/client/logger/Level.kt | 14 +- .../com/nextcloud/client/logger/LogEntry.kt | 22 +- .../com/nextcloud/client/logger/Logger.kt | 2 +- .../com/nextcloud/client/logger/LoggerImpl.kt | 5 +- .../nextcloud/client/logger/LogsRepository.kt | 2 +- .../com/nextcloud/client/logger/ThreadLoop.kt | 2 +- .../nextcloud/client/logger/ui/AsyncFilter.kt | 2 +- .../client/logger/ui/LogsActivity.kt | 10 +- .../nextcloud/client/logger/ui/LogsAdapter.kt | 30 +- .../client/logger/ui/LogsEmailSender.kt | 16 +- .../client/logger/ui/LogsViewModel.kt | 2 +- .../com/nextcloud/client/media/AudioFocus.kt | 2 +- .../client/media/AudioFocusManager.kt | 66 +- .../client/media/BackgroundPlayerService.kt | 241 + .../com/nextcloud/client/media/ErrorFormat.kt | 2 +- .../client/media/ExoplayerListener.kt | 5 +- .../com/nextcloud/client/media/LoadUrlTask.kt | 2 +- .../client/media/NextcloudExoPlayer.kt | 2 +- .../java/com/nextcloud/client/media/Player.kt | 30 +- .../com/nextcloud/client/media/PlayerError.kt | 2 +- .../nextcloud/client/media/PlayerService.kt | 73 +- .../client/media/PlayerServiceConnection.kt | 56 +- .../client/media/PlayerStateMachine.kt | 6 +- .../nextcloud/client/media/PlaylistItem.kt | 2 +- .../client/migrations/MigrationError.kt | 2 +- .../client/migrations/MigrationInfo.kt | 2 +- .../nextcloud/client/migrations/Migrations.kt | 6 +- .../client/migrations/MigrationsDb.kt | 25 +- .../client/migrations/MigrationsManager.kt | 2 +- .../migrations/MigrationsManagerImpl.kt | 2 +- .../nextcloud/client/mixins/ActivityMixin.kt | 2 +- .../nextcloud/client/mixins/MixinRegistry.kt | 2 +- .../nextcloud/client/mixins/SessionMixin.kt | 17 +- .../client/network/ClientFactory.java | 2 +- .../client/network/ClientFactoryImpl.java | 2 +- .../nextcloud/client/network/Connectivity.kt | 2 +- .../client/network/ConnectivityService.java | 25 +- .../network/ConnectivityServiceImpl.java | 73 +- .../client/network/NetworkModule.java | 2 +- .../client/network/WalledCheckCache.kt | 22 +- .../notifications/AppNotificationManager.kt | 2 +- .../AppNotificationManagerImpl.kt | 8 +- .../client/onboarding/FirstRunActivity.kt | 43 +- .../client/onboarding/OnboardingModule.kt | 6 +- .../client/onboarding/OnboardingService.kt | 2 +- .../onboarding/OnboardingServiceImpl.kt | 16 +- .../client/onboarding/WhatsNewActivity.kt | 30 +- .../client/preferences/AppPreferences.java | 27 +- .../preferences/AppPreferencesImpl.java | 71 +- .../client/preferences/DarkMode.java | 2 +- .../client/preferences/PreferencesModule.java | 2 +- .../client/preferences/SubFolderRule.kt | 6 +- .../com/nextcloud/client/utils/IntentUtil.kt | 31 +- .../com/nextcloud/client/utils/Throttler.kt | 2 +- .../DashboardWidgetConfigurationActivity.kt | 2 +- .../DashboardWidgetConfigurationInterface.kt | 2 +- .../client/widget/DashboardWidgetProvider.kt | 2 +- .../client/widget/DashboardWidgetService.kt | 143 +- .../client/widget/DashboardWidgetUpdater.kt | 63 +- .../client/widget/WidgetConfiguration.kt | 2 +- .../client/widget/WidgetRepository.kt | 75 +- .../com/nextcloud/model/HTTPStatusCodes.kt | 5 +- .../com/nextcloud/model/OCFileFilterType.kt | 13 + .../nextcloud/model/OfflineOperationType.kt | 32 + .../nextcloud/model/SearchResultEntryType.kt | 28 + .../java/com/nextcloud/model/ShareeEntry.kt | 71 + .../java/com/nextcloud/model/ToolbarItem.kt | 38 + .../java/com/nextcloud/model/WorkerState.kt | 14 +- .../nextcloud/model/WorkerStateLiveData.kt | 10 +- .../receiver/NetworkChangeReceiver.kt | 29 + .../nextcloud/repository/ClientRepository.kt | 45 + .../repository/RemoteClientRepository.kt | 68 + .../ui/ChooseAccountDialogFragment.kt | 66 +- .../ui/ChooseStorageLocationDialogFragment.kt | 170 + .../java/com/nextcloud/ui/ClearStatusTask.kt | 16 +- .../com/nextcloud/ui/ImageDetailFragment.kt | 45 +- .../java/com/nextcloud/ui/RetrieveStatus.kt | 35 + .../ui/SetOnlineStatusBottomSheet.kt | 160 + .../ui/SetPredefinedCustomStatusTask.kt | 16 +- ...ment.kt => SetStatusMessageBottomSheet.kt} | 156 +- .../java/com/nextcloud/ui/SetStatusTask.kt | 22 +- .../ui/SetUserDefinedCustomStatusTask.kt | 2 +- .../com/nextcloud/ui/SquareLoaderImageView.kt | 2 +- .../nextcloud/ui/behavior/OnScrollBehavior.kt | 36 + .../ui/composeActivity/ComposeActivity.kt | 61 +- .../ui/composeActivity/ComposeDestination.kt | 4 +- .../alertDialog/SimpleAlertDialog.kt | 9 +- .../bottomSheet/MoreActionsBottomSheet.kt | 13 +- .../nextcloud/ui/fileactions/FileAction.kt | 210 +- .../ui/fileactions/FileActionsBottomSheet.kt | 67 +- .../ui/fileactions/FileActionsViewModel.kt | 55 +- .../trashbinFileActions/TrashbinFileAction.kt | 32 + .../TrashbinFileActionsBottomSheet.kt | 234 + .../TrashbinFileActionsViewModel.kt | 96 + .../com/nextcloud/utils/BitmapExtensions.kt | 119 + .../java/com/nextcloud/utils/BuildHelper.kt | 15 + .../nextcloud/utils/CalendarEventManager.kt | 78 + .../com/nextcloud/utils/ContactManager.kt | 144 + .../java/com/nextcloud/utils/EditorUtils.kt | 6 +- .../java/com/nextcloud/utils/FileHelper.kt | 67 + .../utils/ForegroundServiceHelper.kt | 14 +- .../java/com/nextcloud/utils/GlideHelper.kt | 193 + .../java/com/nextcloud/utils/LinkHelper.kt | 135 + .../java/com/nextcloud/utils/MenuUtils.kt | 2 +- .../java/com/nextcloud/utils/OCFileUtils.kt | 56 + .../java/com/nextcloud/utils/ShortcutUtil.kt | 103 +- .../java/com/nextcloud/utils/TimeConstants.kt | 2 +- .../nextcloud/utils/autoRename/AutoRename.kt | 135 + .../nextcloud/utils/date/DateFormatPattern.kt | 20 + .../com/nextcloud/utils/date/DateFormatter.kt | 26 + .../utils/extensions/AccountExtensions.kt | 2 +- .../utils/extensions/ActionBarExtensions.kt | 19 + .../utils/extensions/ActivityExtensions.kt | 18 +- .../utils/extensions/BundleExtensions.kt | 39 +- .../utils/extensions/ContextExtensions.kt | 54 +- .../utils/extensions/DateExtensions.kt | 17 + .../extensions/DecryptedUserExtensions.kt | 22 + .../utils/extensions/DrawableExtensions.kt | 20 + .../extensions/DrawerActivityExtensions.kt | 43 + .../nextcloud/utils/extensions/Extensions.kt | 14 +- .../FileDataStorageManagerExtensions.kt | 40 + .../utils/extensions/FileExtensions.kt | 25 + .../utils/extensions/FragmentExtensions.kt | 30 + .../utils/extensions/ImageViewExtensions.kt | 49 + .../utils/extensions/IntExtensions.kt | 26 + .../utils/extensions/IntentExtensions.kt | 38 +- .../extensions/OCCapabilityExtensions.kt | 59 + .../utils/extensions/OCFileExtensions.kt | 33 + .../utils/extensions/OCShareExtensions.kt | 14 + .../utils/extensions/OCUploadExtensions.kt | 14 + ...nDataTransferProgressListenerExtensions.kt | 14 + .../extensions/OwnCloudClientExtensions.kt | 22 + .../utils/extensions/ParcableExtensions.kt | 20 + .../RemoteOperationResultExtensions.kt | 64 + .../extensions/SearchResultEntryExtensions.kt | 25 + .../utils/extensions/ShareTypeExtensions.kt | 12 + .../utils/extensions/StringExtensions.kt | 41 +- .../extensions/SyncedFolderExtensions.kt | 27 + .../utils/extensions/TextViewExtensions.kt | 4 +- .../ThumbnailsCacheManagerExtensions.kt | 77 + .../utils/extensions/ViewExtensions.kt | 81 +- .../utils/extensions/WorkManagerExtensions.kt | 27 +- .../fileNameValidator/FileNameValidator.kt | 153 + .../java/com/nextcloud/utils/mdm/MDMConfig.kt | 136 + .../utils/numberFormatter/NumberFormatter.kt | 21 + .../utils/view/FastScrollPopupBackground.kt | 14 +- .../nextcloud/utils/view/FastScrollUtils.kt | 7 +- .../com/nmc/android/ui/LauncherActivity.kt | 17 +- .../java/com/owncloud/android/MainApp.java | 269 +- .../authentication/AccountAuthenticator.java | 5 +- .../android/authentication/AuthObject.kt | 12 + .../authentication/AuthenticatorActivity.java | 652 +- .../authentication/AuthenticatorAsyncTask.kt | 8 +- .../authentication/AuthenticatorUrlUtils.kt | 32 +- .../authentication/DeepLinkLoginActivity.kt | 19 +- .../android/authentication/EnforcedServer.kt | 10 + .../android/authentication/LoginUrlInfo.java | 17 - .../android/authentication/LoginUrlInfo.kt | 20 + .../android/authentication/PassCodeManager.kt | 58 +- .../datamodel/ArbitraryDataProvider.kt | 2 +- .../datamodel/ArbitraryDataProviderImpl.java | 2 +- .../datamodel/ContentResolverHelper.kt | 30 +- .../datamodel/DecryptedFolderMetadataOld.java | 2 +- .../android/datamodel/DecryptedPushMessage.kt | 2 +- .../android/datamodel/EncryptedFiledrop.kt | 2 +- .../datamodel/ExternalLinksProvider.java | 42 +- .../datamodel/FileDataStorageManager.java | 624 +- .../android/datamodel/FileSystemDataSet.java | 2 +- .../datamodel/FilesystemDataProvider.java | 215 +- .../datamodel/ForegroundServiceType.kt | 17 +- .../android/datamodel/GalleryItems.kt | 2 +- .../owncloud/android/datamodel/GalleryRow.kt | 12 +- .../owncloud/android/datamodel/MediaFolder.kt | 2 +- .../android/datamodel/MediaFolderType.kt | 10 +- .../android/datamodel/MediaFoldersModel.kt | 2 +- .../android/datamodel/MediaProvider.java | 2 +- .../owncloud/android/datamodel/OCFile.java | 209 +- .../datamodel/PushConfigurationState.java | 2 +- .../android/datamodel/QuickPermissionModel.kt | 13 - .../android/datamodel/ReceiverFlag.kt | 8 +- .../owncloud/android/datamodel/SharesType.kt | 13 + .../datamodel/SignatureVerification.kt | 2 +- .../android/datamodel/SyncedFolder.java | 12 +- .../datamodel/SyncedFolderDisplayItem.java | 2 +- .../datamodel/SyncedFolderProvider.java | 42 +- .../owncloud/android/datamodel/Template.kt | 16 +- .../datamodel/ThumbnailsCacheManager.java | 287 +- .../datamodel/UploadsStorageManager.java | 296 +- .../android/datamodel/VirtualFolderType.java | 2 +- .../datamodel/e2e/v1/decrypted/Data.java | 2 +- .../e2e/v1/decrypted/DecryptedFile.java | 2 +- .../DecryptedFolderMetadataFileV1.java | 2 +- .../e2e/v1/decrypted/DecryptedMetadata.java | 2 +- .../datamodel/e2e/v1/decrypted/Encrypted.java | 2 +- .../datamodel/e2e/v1/decrypted/Sharing.java | 2 +- .../e2e/v1/encrypted/EncryptedFile.kt | 2 +- .../EncryptedFolderMetadataFileV1.java | 2 +- .../e2e/v2/decrypted/DecryptedFile.kt | 2 +- .../decrypted/DecryptedFolderMetadataFile.kt | 2 +- .../e2e/v2/decrypted/DecryptedMetadata.kt | 2 +- .../e2e/v2/decrypted/DecryptedUser.kt | 7 +- .../e2e/v2/encrypted/EncryptedFiledrop.kt | 2 +- .../e2e/v2/encrypted/EncryptedFiledropUser.kt | 7 +- .../encrypted/EncryptedFolderMetadataFile.kt | 2 +- .../e2e/v2/encrypted/EncryptedMetadata.kt | 8 +- .../e2e/v2/encrypted/EncryptedUser.kt | 8 +- .../e2e/v2/encrypted/FiledropData.kt | 2 +- .../quickPermission/QuickPermission.kt | 10 + .../quickPermission/QuickPermissionType.kt | 51 + .../datastorage/DataStorageProvider.java | 2 +- .../android/datastorage/StoragePoint.java | 2 +- .../datastorage/UniqueStorageList.java | 2 +- .../AbstractCommandLineStoragePoint.java | 4 +- .../AbstractStoragePointProvider.java | 2 +- .../EnvironmentStoragePointProvider.java | 2 +- .../HardcodedStoragePointProvider.java | 2 +- .../providers/IStoragePointProvider.java | 2 +- .../MountCommandStoragePointProvider.java | 4 +- .../SystemDefaultStoragePointProvider.java | 2 +- .../providers/VDCStoragePointProvider.java | 9 +- .../com/owncloud/android/db/OCUpload.java | 7 +- .../owncloud/android/db/OCUploadComparator.kt | 20 +- .../com/owncloud/android/db/ProviderMeta.java | 179 +- .../com/owncloud/android/db/UploadResult.java | 169 +- .../android/features/FeatureItem.java | 2 +- .../files/BootupBroadcastReceiver.java | 11 +- .../CreateFileFromTemplateOperation.java | 2 +- .../android/files/FetchTemplateOperation.java | 2 +- .../android/files/FileMenuFilter.java | 53 +- .../files/StreamMediaFileOperation.java | 2 +- .../android/files/services/IndexedForest.java | 18 +- .../files/services/NameCollisionPolicy.java | 2 +- .../android/media/MediaControlView.java | 343 - .../android/media/MediaControlView.kt | 355 + .../CheckCurrentCredentialsOperation.java | 2 +- .../operations/CommentFileOperation.java | 10 +- .../android/operations/CopyFileOperation.java | 15 +- .../operations/CreateFolderOperation.java | 59 +- .../CreateShareViaLinkOperation.java | 8 +- .../CreateShareWithShareeOperation.java | 19 +- .../DetectAuthenticationMethodOperation.java | 24 +- .../operations/DownloadFileOperation.java | 29 +- .../android/operations/DownloadType.kt | 6 +- .../operations/GetCapabilitiesOperation.java | 2 +- .../GetFilesDownloadLimitOperation.kt | 30 + .../operations/GetServerInfoOperation.java | 24 +- .../operations/GetSharesForFileOperation.java | 80 - .../operations/GetSharesForFileOperation.kt | 68 + .../operations/GetUserProfileOperation.java | 2 +- .../android/operations/MoveFileOperation.java | 2 +- .../operations/RefreshFolderOperation.java | 203 +- .../RemoteOperationFailedException.java | 2 +- .../operations/RemoveFileOperation.java | 116 - .../android/operations/RemoveFileOperation.kt | 99 + .../RemoveRemoteEncryptedFileOperation.kt | 4 +- .../operations/RenameFileOperation.java | 2 +- .../RichDocumentsCreateAssetOperation.java | 2 +- .../operations/RichDocumentsUrlOperation.java | 2 +- .../SetFilesDownloadLimitOperation.kt | 55 + .../operations/SynchronizeFileOperation.java | 59 +- .../SynchronizeFolderOperation.java | 141 +- .../android/operations/UnshareOperation.java | 14 +- .../UpdateNoteForShareOperation.java | 2 +- .../operations/UpdateOCVersionOperation.java | 2 +- .../operations/UpdateShareInfoOperation.java | 37 +- .../UpdateSharePermissionsOperation.java | 4 +- .../UpdateShareViaLinkOperation.java | 2 +- .../android/operations/UploadException.java | 2 +- .../operations/UploadFileOperation.java | 819 +- .../operations/common/SyncOperation.java | 5 +- .../android/operations/e2e/E2EClientData.kt | 12 + .../android/operations/e2e/E2EData.kt | 17 + .../android/operations/e2e/E2EFiles.kt | 46 + .../upload/UploadFileBroadcastReceiver.kt | 61 + .../UploadFileBroadcastReceiverActions.kt | 13 + .../operations/upload/UploadFileException.kt | 13 + .../upload/UploadFileOperationExtensions.kt | 76 + .../DiskLruImageCacheFileProvider.java | 2 +- .../providers/DocumentsStorageProvider.java | 147 +- .../providers/FileContentProvider.java | 207 +- .../providers/UsersAndGroupsSearchConfig.kt | 2 +- .../UsersAndGroupsSearchProvider.java | 26 +- .../services/AccountManagerService.java | 2 +- .../android/services/OperationsService.java | 91 +- .../android/services/SyncFolderHandler.java | 2 +- .../AbstractOwnCloudSyncAdapter.java | 2 +- .../android/syncadapter/FileSyncAdapter.java | 16 +- .../android/syncadapter/FileSyncService.java | 2 +- .../android/ui/AvatarGroupLayout.java | 192 - .../owncloud/android/ui/AvatarGroupLayout.kt | 182 + .../owncloud/android/ui/CompletionCallback.kt | 12 + .../android/ui/EmptyRecyclerView.java | 2 +- .../android/ui/ListPreferenceDialog.kt | 44 + .../android/ui/NextcloudWebViewClient.kt | 2 +- .../owncloud/android/ui/SquareImageView.java | 2 +- .../android/ui/SquareLinearLayout.java | 2 +- .../owncloud/android/ui/StatusDrawable.java | 19 +- .../com/owncloud/android/ui/TextDrawable.java | 2 +- .../android/ui/ThemeableSwitchPreference.java | 6 +- .../ui/activities/ActivitiesActivity.java | 32 +- .../ui/activities/ActivitiesContract.java | 6 +- .../ui/activities/ActivitiesPresenter.java | 6 +- .../StickyHeaderItemDecoration.java | 2 +- .../data/activities/ActivitiesRepository.java | 6 +- .../data/activities/ActivitiesServiceApi.java | 6 +- .../activities/ActivitiesServiceApiImpl.java | 14 +- .../data/activities/ActivityRepositories.java | 2 +- .../RemoteActivitiesRepository.java | 24 +- .../data/files/FileRepositories.java | 2 +- .../data/files/FilesRepository.java | 2 +- .../data/files/FilesServiceApi.java | 2 +- .../data/files/FilesServiceApiImpl.java | 2 +- .../data/files/RemoteFilesRepository.java | 22 +- .../android/ui/activity/BaseActivity.java | 24 +- .../activity/ChooseStorageLocationActivity.kt | 38 + .../android/ui/activity/CommunityActivity.kt | 11 +- .../android/ui/activity/ComponentsGetter.java | 4 +- .../ui/activity/ConflictsResolveActivity.kt | 336 +- .../activity/ContactsPreferenceActivity.java | 2 +- .../ui/activity/CopyToClipboardActivity.kt | 4 +- .../android/ui/activity/DrawerActivity.java | 687 +- .../android/ui/activity/EditorWebView.java | 60 +- .../ErrorsWhileCopyingHandlerActivity.java | 2 +- .../ui/activity/ExternalSiteWebView.java | 16 +- .../android/ui/activity/FileActivity.java | 209 +- .../ui/activity/FileDisplayActivity.java | 2504 -- .../ui/activity/FileDisplayActivity.kt | 3056 +++ .../android/ui/activity/FilePickerActivity.kt | 2 +- .../ui/activity/FolderPickerActivity.kt | 182 +- .../ui/activity/InternalTwoWaySyncActivity.kt | 236 + .../ui/activity/ManageAccountsActivity.java | 475 - .../ui/activity/ManageAccountsActivity.kt | 497 + .../ui/activity/ManageSpaceActivity.kt | 150 +- .../ui/activity/NotificationsActivity.kt | 145 +- .../OnEnforceableRefreshListener.java | 2 +- .../android/ui/activity/PassCodeActivity.kt | 132 +- .../ReceiveExternalFilesActivity.java | 228 +- .../activity/RequestCredentialsActivity.java | 18 +- .../ui/activity/RichDocumentsEditorWebView.kt | 11 +- .../android/ui/activity/SettingsActivity.java | 293 +- .../ui/activity/SetupEncryptionActivity.kt | 4 +- .../android/ui/activity/ShareActivity.java | 4 +- .../activity/SsoGrantPermissionActivity.java | 2 +- .../android/ui/activity/StorageMigration.java | 179 +- .../ui/activity/SyncedFoldersActivity.kt | 160 +- .../android/ui/activity/TextEditorWebView.kt | 6 +- .../android/ui/activity/ToolbarActivity.java | 55 +- .../ui/activity/UploadFilesActivity.java | 100 +- .../ui/activity/UploadListActivity.java | 71 +- .../android/ui/activity/UserInfoActivity.java | 120 +- .../ActivityAndVersionListAdapter.java | 10 +- .../ui/adapter/ActivityListAdapter.java | 72 +- ...rtificateCombinedExceptionViewAdapter.java | 2 +- .../CommonOCFileListAdapterInterface.kt | 4 +- .../ui/adapter/DashboardWidgetListAdapter.kt | 14 +- .../android/ui/adapter/DiskLruImageCache.java | 10 +- .../ui/adapter/FeaturesViewAdapter.java | 20 +- .../ui/adapter/FeaturesWebViewAdapter.java | 18 +- .../ui/adapter/FileDetailTabAdapter.java | 50 +- .../ui/adapter/FilterableListAdapter.java | 2 +- .../android/ui/adapter/GalleryAdapter.kt | 166 +- .../ui/adapter/GalleryHeaderViewHolder.kt | 2 +- .../android/ui/adapter/GalleryRowHolder.kt | 238 +- ...stAdapter.kt => GroupFolderListAdapter.kt} | 32 +- .../ui/adapter/InternalShareViewHolder.java | 2 +- .../ui/adapter/InternalTwoWaySyncAdapter.kt | 54 + .../adapter/InternalTwoWaySyncViewHolder.kt | 56 + .../ui/adapter/LinkShareViewHolder.java | 97 - .../android/ui/adapter/LinkShareViewHolder.kt | 181 + .../ui/adapter/ListGridItemViewHolder.kt | 5 +- .../android/ui/adapter/ListItemViewHolder.kt | 2 +- ...idImageViewHolder.kt => ListViewHolder.kt} | 4 +- .../ui/adapter/LocalFileListAdapter.java | 239 +- .../ui/adapter/NewLinkShareViewHolder.java | 2 +- .../ui/adapter/NewSecureFileDropViewHolder.kt | 2 +- .../ui/adapter/NotificationListAdapter.java | 61 +- .../android/ui/adapter/OCFileListAdapter.java | 584 +- .../android/ui/adapter/OCFileListDelegate.kt | 360 +- .../ui/adapter/OCFileListFooterViewHolder.kt | 9 +- .../adapter/OCFileListGridItemViewHolder.kt | 20 +- .../ui/adapter/OCFileListHeaderViewHolder.kt | 9 +- .../ui/adapter/OCFileListItemViewHolder.kt | 4 +- .../OCFileListRecommendedItemViewHolder.kt | 49 + ...eViewHolder.kt => OCFileListViewHolder.kt} | 21 +- .../ui/adapter/OCShareToOCFileConverter.kt | 22 +- .../adapter/PredefinedStatusClickListener.kt | 2 +- .../ui/adapter/PredefinedStatusListAdapter.kt | 12 +- .../ui/adapter/PredefinedStatusViewHolder.kt | 2 +- .../android/ui/adapter/PrintAdapter.java | 22 +- .../android/ui/adapter/ProgressListener.java | 56 - .../adapter/QuickSharingPermissionsAdapter.kt | 54 +- .../ui/adapter/ReceiveExternalFilesAdapter.kt | 6 +- .../ui/adapter/RecommendedFilesAdapter.kt | 36 + .../adapter/RichDocumentsTemplateAdapter.java | 70 +- .../android/ui/adapter/SendButtonAdapter.java | 2 +- .../android/ui/adapter/ShareViewHolder.java | 109 +- .../android/ui/adapter/ShareeListAdapter.java | 224 - .../android/ui/adapter/ShareeListAdapter.kt | 189 + .../ui/adapter/ShareeListAdapterListener.java | 2 +- .../ui/adapter/SslCertificateViewAdapter.java | 2 +- .../ui/adapter/SslErrorViewAdapter.java | 2 +- .../ui/adapter/StickyHeaderAdapter.java | 2 +- .../android/ui/adapter/StoragePathAdapter.kt | 14 +- .../android/ui/adapter/StoragePathItem.java | 2 +- .../ui/adapter/SyncedFolderAdapter.java | 454 - .../android/ui/adapter/SyncedFolderAdapter.kt | 467 + .../android/ui/adapter/TemplateAdapter.java | 43 +- .../ui/adapter/TrashbinListAdapter.java | 113 +- .../adapter/UnifiedSearchFooterViewHolder.kt | 5 +- .../adapter/UnifiedSearchHeaderViewHolder.kt | 5 +- .../ui/adapter/UnifiedSearchItemViewHolder.kt | 123 +- .../ui/adapter/UnifiedSearchListAdapter.kt | 50 +- .../android/ui/adapter/UploadListAdapter.java | 369 +- .../android/ui/adapter/UserListAdapter.java | 23 +- .../android/ui/adapter/UserListItem.java | 2 +- .../owncloud/android/ui/adapter/ViewType.java | 17 - .../ui/adapter/WidgetListItemViewHolder.kt | 44 +- .../adapter/X509CertificateViewAdapter.java | 2 +- .../DownloadProgressListener.kt | 12 + .../progressListener/ProgressListener.kt | 35 + .../UploadProgressListener.kt | 18 + .../asynctasks/CheckAvailableSpaceTask.java | 2 +- .../ui/asynctasks/CheckRemoteWipeTask.java | 2 +- .../CopyAndUploadContentUrisTask.java | 162 +- .../DeleteAllNotificationsTask.java | 10 +- .../ui/asynctasks/DeleteNotificationTask.java | 8 +- .../ui/asynctasks/FetchRemoteFileTask.java | 8 +- .../ui/asynctasks/GallerySearchTask.java | 10 +- .../ui/asynctasks/GetRemoteFileTask.kt | 2 +- .../ui/asynctasks/GroupfoldersSearchTask.kt | 2 +- .../ui/asynctasks/LoadContactsTask.java | 12 +- .../asynctasks/LoadingVersionNumberTask.java | 2 +- .../NotificationExecuteActionTask.java | 65 +- .../android/ui/asynctasks/PrintAsyncTask.java | 2 +- .../RetrieveHoverCardAsyncTask.java | 2 +- .../asynctasks/RetrieveStatusAsyncTask.java | 63 - .../asynctasks/RichDocumentsLoadUrlTask.java | 2 +- .../ui/asynctasks/TextEditorLoadUrlTask.java | 8 +- .../ui/components/CustomViewPager.java | 47 - .../android/ui/components/PassCodeEditText.kt | 4 +- .../android/ui/components/SendButtonData.java | 2 +- .../decoration/MediaGridItemDecoration.java | 2 +- .../SimpleListItemDividerDecoration.java | 68 - .../ui/dialog/AccountChooserInterface.kt | 2 +- .../android/ui/dialog/AccountRemovalDialog.kt | 13 +- ...ooseRichDocumentsTemplateDialogFragment.kt | 112 +- .../ui/dialog/ChooseTemplateDialogFragment.kt | 290 +- .../ui/dialog/ConfirmationDialogFragment.kt | 57 +- .../ui/dialog/ConflictsResolveDialog.java | 269 - .../ui/dialog/ConflictsResolveDialog.kt | 373 + .../ui/dialog/CreateFolderDialogFragment.kt | 189 +- .../ExpirationDatePickerDialogFragment.kt | 9 +- .../ui/dialog/IndeterminateProgressDialog.kt | 8 +- .../android/ui/dialog/LoadingDialog.kt | 34 +- .../LocalStoragePathPickerDialogFragment.kt | 6 +- .../ui/dialog/MultipleAccountsDialog.kt | 11 +- .../ui/dialog/RemoveFilesDialogFragment.java | 201 - .../ui/dialog/RemoveFilesDialogFragment.kt | 229 + .../ui/dialog/RenameFileDialogFragment.java | 219 - .../ui/dialog/RenameFileDialogFragment.kt | 236 + .../RenamePublicShareDialogFragment.java | 126 - .../android/ui/dialog/SendFilesDialog.kt | 20 +- .../android/ui/dialog/SendShareDialog.kt | 29 +- .../android/ui/dialog/ShareLinkToDialog.java | 171 - .../android/ui/dialog/ShareLinkToDialog.kt | 149 + .../ui/dialog/SharePasswordDialogFragment.kt | 13 +- .../ui/dialog/SortingOrderDialogFragment.kt | 20 +- .../ui/dialog/SslUntrustedCertDialog.kt | 9 +- .../dialog/StoragePermissionDialogFragment.kt | 17 +- .../SyncFileNotEnoughSpaceDialogFragment.kt | 9 +- .../SyncedFolderPreferencesDialogFragment.kt | 43 +- .../android/ui/dialog/TermsOfServiceDialog.kt | 153 + .../ui/dialog/parcel/ConflictDialogData.kt | 65 + .../dialog/parcel/SyncedFolderParcelable.java | 19 +- .../setupEncryption/CertificateValidator.kt | 57 + .../SetupEncryptionDialogFragment.kt | 278 +- .../android/ui/events/AccountRemovedEvent.kt | 2 +- .../android/ui/events/ChangeMenuEvent.kt | 2 +- .../android/ui/events/CommentsEvent.kt | 2 +- .../owncloud/android/ui/events/DialogEvent.kt | 14 + .../android/ui/events/DummyDrawerEvent.kt | 2 +- .../android/ui/events/EncryptionEvent.kt | 9 +- .../android/ui/events/EventBusFactory.kt | 18 + .../android/ui/events/FavoriteEvent.kt | 2 +- .../ui/events/FileDownloadProgressEvent.kt | 10 + .../android/ui/events/FileLockEvent.kt | 4 +- .../owncloud/android/ui/events/SearchEvent.kt | 2 +- .../android/ui/events/SyncEventFinished.kt | 2 +- .../android/ui/events/TokenPushEvent.kt | 4 +- .../android/ui/events/VCardToggleEvent.kt | 2 +- .../ui/fragment/ExtendedListFragment.java | 705 - .../ui/fragment/ExtendedListFragment.kt | 808 + .../android/ui/fragment/FeatureFragment.java | 2 +- .../ui/fragment/FeatureWebFragment.java | 2 +- .../FileDetailActivitiesFragment.java | 36 +- .../ui/fragment/FileDetailFragment.java | 294 +- .../fragment/FileDetailSharingFragment.java | 267 +- ...ileDetailSharingMenuBottomSheetDialog.java | 35 +- ...eDetailsSharingMenuBottomSheetActions.java | 2 +- .../FileDetailsSharingProcessFragment.kt | 800 +- .../android/ui/fragment/FileFragment.java | 4 +- .../android/ui/fragment/GalleryFragment.java | 52 +- .../GalleryFragmentBottomSheetActions.kt | 2 +- .../GalleryFragmentBottomSheetDialog.kt | 8 +- .../ui/fragment/GroupfolderListFragment.kt | 69 +- .../ui/fragment/LocalFileListFragment.java | 119 +- .../OCFileListBottomSheetActions.java | 2 +- .../fragment/OCFileListBottomSheetDialog.java | 217 - .../fragment/OCFileListBottomSheetDialog.kt | 233 + .../ui/fragment/OCFileListFragment.java | 1085 +- .../ui/fragment/OCFileListSearchAsyncTask.kt | 83 - .../ui/fragment/OCFileListSearchTask.kt | 95 + .../ui/fragment/ProfileBottomSheetDialog.kt | 13 +- ...ckSharingPermissionsBottomSheetDialog.java | 87 +- .../android/ui/fragment/SearchType.kt | 14 +- .../android/ui/fragment/SharedListFragment.kt | 76 +- .../ui/fragment/UnifiedSearchFragment.kt | 159 +- .../fragment/contactsbackup/BackupFragment.kt | 179 +- .../contactsbackup/BackupListAdapter.kt | 142 +- .../contactsbackup/BackupListFragment.java | 59 +- .../BackupListHeaderViewHolder.kt | 8 +- .../BackupListItemViewHolder.kt | 2 +- .../CalendarItemViewHolder.java | 2 +- .../contactsbackup/ContactItemViewHolder.kt | 2 +- .../contactsbackup/ContactsAccount.java | 5 +- .../contactsbackup/VCardComparator.java | 2 +- .../filesRepository/FilesRepository.kt | 29 + .../filesRepository/RemoteFilesRepository.kt | 87 + .../fragment/share/RemoteShareRepository.kt | 59 + .../ui/fragment/share/ShareRepository.kt | 12 + .../util/FileDetailSharingFragmentHelper.java | 2 +- .../util/GalleryFastScrollViewHelper.kt | 20 +- .../ui/fragment/util/PairMediatorLiveData.kt | 2 +- .../fragment/util/SharePermissionManager.kt | 162 + .../ui/fragment/util/SharingMenuHelper.java | 166 - .../ui/helpers/FileOperationsHelper.java | 379 +- .../helpers/SparseBooleanArrayParcelable.java | 36 +- .../android/ui/helpers/UriUploader.kt | 51 +- .../ui/interfaces/ActivityListInterface.java | 2 +- .../ui/interfaces/GroupfolderListInterface.kt | 2 +- .../LocalFileListFragmentInterface.java | 2 +- .../OCFileListFragmentInterface.java | 2 +- .../ui/interfaces/TransactionInterface.kt | 14 + .../interfaces/TrashbinActivityInterface.java | 12 +- .../interfaces/UnifiedSearchListInterface.kt | 3 +- .../ui/interfaces/VersionListInterface.java | 2 +- .../ui/notifications/NotificationUtils.java | 83 - .../ui/notifications/NotificationUtils.kt | 32 + .../notifications/NotificationsContract.java | 2 +- .../ui/preview/FileDownloadFragment.java | 92 +- .../ui/preview/PreviewBitmapActivity.kt | 8 +- .../ui/preview/PreviewImageActivity.java | 539 - .../ui/preview/PreviewImageActivity.kt | 593 + .../ui/preview/PreviewImageErrorFragment.java | 40 - .../ui/preview/PreviewImageErrorFragment.kt | 35 + .../ui/preview/PreviewImageFragment.java | 834 - .../ui/preview/PreviewImageFragment.kt | 846 + .../ui/preview/PreviewImagePagerAdapter.kt | 55 +- .../ui/preview/PreviewMediaActivity.kt | 452 +- .../ui/preview/PreviewMediaFragment.java | 695 - .../ui/preview/PreviewMediaFragment.kt | 635 + .../ui/preview/PreviewTextFileFragment.java | 37 +- .../ui/preview/PreviewTextFragment.java | 229 - .../android/ui/preview/PreviewTextFragment.kt | 251 + .../ui/preview/PreviewTextStringFragment.java | 14 +- .../preview/PreviewVideoFullscreenDialog.kt | 22 +- .../model/PreviewImageActivityState.kt | 14 + .../ui/preview/pdf/PreviewPdfAdapter.kt | 14 +- .../ui/preview/pdf/PreviewPdfFragment.kt | 13 +- .../ui/preview/pdf/PreviewPdfViewModel.kt | 6 +- .../ui/trashbin/RemoteTrashbinRepository.kt | 82 +- .../android/ui/trashbin/TrashbinActivity.kt | 401 +- .../android/ui/trashbin/TrashbinContract.kt | 7 +- .../android/ui/trashbin/TrashbinPresenter.kt | 51 +- .../android/ui/trashbin/TrashbinRepository.kt | 2 +- .../unifiedsearch/GetSearchProvidersTask.kt | 6 +- .../unifiedsearch/IUnifiedSearchRepository.kt | 2 +- .../unifiedsearch/IUnifiedSearchViewModel.kt | 2 +- .../ui/unifiedsearch/SearchOnProviderTask.kt | 2 +- .../ui/unifiedsearch/UnifiedSearchModel.kt | 9 +- .../UnifiedSearchRemoteRepository.kt | 2 +- .../unifiedsearch/UnifiedSearchViewModel.kt | 52 +- .../ui/whatsnew/ProgressIndicator.java | 2 +- .../owncloud/android/utils/BitmapUtils.java | 234 +- .../owncloud/android/utils/ClipboardUtil.kt | 80 +- .../android/utils/DataHolderUtil.java | 6 +- .../android/utils/DeviceCredentialUtils.java | 2 +- .../android/utils/DialogMenuItem.java | 2 +- .../owncloud/android/utils/DisplayUtils.java | 455 +- .../owncloud/android/utils/DrawableUtil.kt | 58 +- .../android/utils/DrawerMenuUtil.java | 2 +- .../android/utils/EncryptionUtils.java | 130 +- .../android/utils/EncryptionUtilsV2.kt | 244 +- .../android/utils/ErrorMessageAdapter.java | 2 +- .../owncloud/android/utils/FileExportUtils.kt | 10 +- .../owncloud/android/utils/FileSortOrder.kt | 76 +- .../android/utils/FileSortOrderByDate.kt | 2 +- .../android/utils/FileSortOrderByName.kt | 2 +- .../android/utils/FileSortOrderBySize.kt | 10 +- .../android/utils/FileStorageUtils.java | 192 +- .../com/owncloud/android/utils/FileUtil.java | 27 +- .../android/utils/FilesSyncHelper.java | 278 +- .../owncloud/android/utils/KeyboardUtils.kt | 2 +- .../com/owncloud/android/utils/MimeType.java | 2 +- .../owncloud/android/utils/MimeTypeUtil.java | 35 +- .../android/utils/NextcloudServer.java | 2 +- .../android/utils/OwnCloudSession.java | 2 +- .../com/owncloud/android/utils/PathUtils.kt | 6 +- .../owncloud/android/utils/PermissionUtil.kt | 77 +- .../android/utils/ReceiversHelper.java | 45 +- .../android/utils/ScreenshotTest.java | 2 +- .../owncloud/android/utils/StringUtils.java | 2 +- .../android/utils/SyncedFolderUtils.kt | 99 +- .../com/owncloud/android/utils/UriUtils.kt | 2 +- .../com/owncloud/android/utils/WebViewUtil.kt | 88 +- .../android/utils/appConfig/AppConfigKeys.kt | 24 + .../android/utils/crypto/CryptoError.kt | 14 + .../android/utils/crypto/CryptoHelper.kt | 179 + .../android/utils/crypto/CryptoStringUtils.kt | 20 + .../utils/glide/CustomGlideStreamLoader.java | 34 - .../utils/glide/CustomGlideUriLoader.java | 36 - .../utils/glide/CustomGlideUriLoader.kt | 26 + .../utils/glide/GlideStringStreamFetcher.kt | 77 + .../utils/glide/GlideStringStreamLoader.kt | 24 + .../android/utils/glide/HttpStreamFetcher.kt | 85 +- .../utils/glide/NextcloudGlideModule.kt | 42 + .../utils/glide/StringModelLoaderFactory.kt | 18 + .../utils/glide/UriModelLoaderFactory.kt | 20 + .../android/utils/svg/MenuSimpleTarget.java | 27 - .../android/utils/svg/SVGorImage.java | 30 - .../owncloud/android/utils/svg/SVGorImage.kt | 13 + .../utils/svg/SvgBitmapTranscoder.java | 57 - .../android/utils/svg/SvgBitmapTranscoder.kt | 44 + .../android/utils/svg/SvgDecoder.java | 49 - .../owncloud/android/utils/svg/SvgDecoder.kt | 38 + .../utils/svg/SvgDrawableTranscoder.java | 56 - .../utils/svg/SvgDrawableTranscoder.kt | 31 + .../utils/svg/SvgOrImageBitmapTranscoder.java | 65 - .../utils/svg/SvgOrImageBitmapTranscoder.kt | 52 + .../android/utils/svg/SvgOrImageDecoder.java | 77 - .../android/utils/svg/SvgOrImageDecoder.kt | 62 + .../utils/svg/SvgSoftwareLayerSetter.java | 38 - .../utils/svg/SvgSoftwareLayerSetter.kt | 52 + .../android/utils/theme/CapabilityUtils.java | 7 +- .../theme/FilesSpecificViewThemeUtils.kt | 61 +- .../utils/theme/MaterialSchemesProvider.kt | 2 +- .../theme/MaterialSchemesProviderImpl.kt | 14 +- .../android/utils/theme/ServerThemeImpl.kt | 4 +- .../android/utils/theme/ThemeColorUtils.java | 2 +- .../android/utils/theme/ThemeUtils.java | 2 +- .../android/utils/theme/ViewThemeUtils.kt | 20 +- .../providers/cursors/FileCursor.java | 2 +- .../providers/cursors/RootCursor.java | 2 +- .../third_parties/aosp/SQLiteTokenizer.java | 101 +- .../ezvcard_android/AndroidCustomField.java | 2 +- .../ezvcard_android/ContactOperations.java | 62 +- .../ezvcard_android/DataMappings.java | 115 +- .../sufficientlysecure/AndroidCalendar.java | 8 +- .../sufficientlysecure/CalendarSource.java | 4 +- .../sufficientlysecure/ProcessVEvent.java | 60 +- .../sufficientlysecure/SaveCalendar.java | 52 +- app/src/main/res/anim/blink.xml | 13 + .../res/animator/appbar_elevation_off.xml | 2 +- .../main/res/animator/appbar_elevation_on.xml | 2 +- .../main/res/color/card_border_selector.xml | 11 + .../main/res/color/menu_item_text_color.xml | 2 +- .../res/drawable-night/ic_battery_alert.xml | 8 +- .../preview_markdown_gradient_shape.xml | 2 +- .../main/res/drawable-night/round_bgnd.xml | 2 +- .../res/drawable-night/shared_via_users.xml | 11 +- .../res/drawable/account_circle_white.xml | 15 +- .../main/res/drawable/add_to_home_screen.xml | 4 +- app/src/main/res/drawable/all_files.xml | 15 +- app/src/main/res/drawable/arrow_right.xml | 16 +- app/src/main/res/drawable/backrepeat.xml | 2 +- app/src/main/res/drawable/borderless_btn.xml | 2 +- app/src/main/res/drawable/chat_bubble.xml | 16 + app/src/main/res/drawable/divider.xml | 2 +- app/src/main/res/drawable/e2e_border.xml | 2 +- app/src/main/res/drawable/file_calendar.xml | 13 +- app/src/main/res/drawable/file_sound.xml | 2 +- app/src/main/res/drawable/file_whiteboard.xml | 15 + app/src/main/res/drawable/file_zip.xml | 2 +- .../main/res/drawable/first_run_groupware.xml | 2 +- app/src/main/res/drawable/first_run_talk.xml | 2 +- app/src/main/res/drawable/ic_account_plus.xml | 10 +- .../res/drawable/ic_action_cancel_grey.xml | 6 +- .../res/drawable/ic_action_create_dir.xml | 4 +- .../res/drawable/ic_action_delete_grey.xml | 12 +- .../main/res/drawable/ic_action_refresh.xml | 13 +- .../main/res/drawable/ic_action_upload.xml | 13 +- app/src/main/res/drawable/ic_activity.xml | 21 +- app/src/main/res/drawable/ic_alert.xml | 12 +- app/src/main/res/drawable/ic_arrow_back.xml | 13 +- .../res/drawable/ic_arrow_back_foreground.xml | 16 + app/src/main/res/drawable/ic_arrow_up.xml | 12 +- app/src/main/res/drawable/ic_assistant.xml | 18 +- .../ic_baseline_arrow_drop_down_24.xml | 2 +- .../res/drawable/ic_baseline_check_24.xml | 16 - .../main/res/drawable/ic_battery_alert.xml | 8 +- app/src/main/res/drawable/ic_camera.xml | 13 +- app/src/main/res/drawable/ic_cancel.xml | 13 +- app/src/main/res/drawable/ic_check.xml | 11 +- app/src/main/res/drawable/ic_check_circle.xml | 8 +- .../res/drawable/ic_check_circle_outline.xml | 4 +- .../drawable/ic_checkbox_blank_outline.xml | 13 +- .../main/res/drawable/ic_checkbox_marked.xml | 13 +- app/src/main/res/drawable/ic_circles.xml | 12 +- app/src/main/res/drawable/ic_clock.xml | 19 +- app/src/main/res/drawable/ic_close.xml | 13 +- .../main/res/drawable/ic_cloud_download.xml | 6 +- app/src/main/res/drawable/ic_cloud_sync.xml | 16 + .../main/res/drawable/ic_cloud_sync_off.xml | 13 +- .../main/res/drawable/ic_cloud_sync_on.xml | 13 +- app/src/main/res/drawable/ic_cloud_upload.xml | 6 +- app/src/main/res/drawable/ic_comment.xml | 7 +- app/src/main/res/drawable/ic_comment_grid.xml | 11 +- app/src/main/res/drawable/ic_contact_book.xml | 8 +- .../res/drawable/ic_custom_permissions.xml | 16 + app/src/main/res/drawable/ic_dashboard.xml | 22 +- app/src/main/res/drawable/ic_deck.xml | 2 +- app/src/main/res/drawable/ic_decrypt.xml | 14 +- app/src/main/res/drawable/ic_delete.xml | 10 +- .../main/res/drawable/ic_dots_vertical.xml | 13 +- app/src/main/res/drawable/ic_edit.xml | 17 +- app/src/main/res/drawable/ic_email.xml | 13 +- app/src/main/res/drawable/ic_encrypt.xml | 12 +- app/src/main/res/drawable/ic_expand_less.xml | 16 + app/src/main/res/drawable/ic_expand_more.xml | 6 +- app/src/main/res/drawable/ic_export.xml | 13 +- app/src/main/res/drawable/ic_external.xml | 15 +- app/src/main/res/drawable/ic_eye.xml | 16 + app/src/main/res/drawable/ic_fast_forward.xml | 16 + app/src/main/res/drawable/ic_fast_rewind.xml | 16 + app/src/main/res/drawable/ic_file_request.xml | 16 + app/src/main/res/drawable/ic_find_in_page.xml | 16 + .../main/res/drawable/ic_folder_offline.xml | 16 + .../ic_folder_overlay_account_group.xml | 14 +- .../drawable/ic_folder_overlay_external.xml | 15 +- .../res/drawable/ic_folder_overlay_key.xml | 14 +- .../res/drawable/ic_folder_overlay_link.xml | 11 +- .../res/drawable/ic_folder_overlay_lock.xml | 14 +- .../res/drawable/ic_folder_overlay_share.xml | 10 +- .../res/drawable/ic_folder_overlay_upload.xml | 14 +- app/src/main/res/drawable/ic_global_pause.xml | 9 +- .../main/res/drawable/ic_global_resume.xml | 9 +- app/src/main/res/drawable/ic_group.xml | 13 +- app/src/main/res/drawable/ic_history.xml | 13 +- app/src/main/res/drawable/ic_home.xml | 17 +- .../main/res/drawable/ic_image_grey600.xml | 13 +- .../main/res/drawable/ic_image_outline.xml | 15 +- app/src/main/res/drawable/ic_import.xml | 13 +- app/src/main/res/drawable/ic_info.xml | 7 +- .../res/drawable/ic_information_outline.xml | 15 +- .../res/drawable/ic_launcher_background.xml | 2 +- app/src/main/res/drawable/ic_link.xml | 4 +- .../main/res/drawable/ic_list_empty_error.xml | 4 +- .../res/drawable/ic_list_empty_recent.xml | 15 +- .../res/drawable/ic_list_empty_shared.xml | 15 +- app/src/main/res/drawable/ic_lock.xml | 15 +- .../res/drawable/ic_locked_dots_small.xml | 17 +- app/src/main/res/drawable/ic_map_marker.xml | 12 +- app/src/main/res/drawable/ic_more_apps.xml | 26 +- app/src/main/res/drawable/ic_no_internet.xml | 17 + app/src/main/res/drawable/ic_notes.xml | 2 +- app/src/main/res/drawable/ic_notification.xml | 4 +- app/src/main/res/drawable/ic_ok.xml | 13 +- app/src/main/res/drawable/ic_pause.xml | 15 + app/src/main/res/drawable/ic_people.xml | 15 +- app/src/main/res/drawable/ic_phone.xml | 12 +- app/src/main/res/drawable/ic_play.xml | 15 + app/src/main/res/drawable/ic_rename.xml | 12 +- app/src/main/res/drawable/ic_retry.xml | 16 + app/src/main/res/drawable/ic_save.xml | 13 +- .../main/res/drawable/ic_scan_document.xml | 13 +- app/src/main/res/drawable/ic_sd.xml | 15 +- app/src/main/res/drawable/ic_sd_grey600.xml | 13 +- app/src/main/res/drawable/ic_search.xml | 13 +- app/src/main/res/drawable/ic_search_grey.xml | 13 +- .../res/drawable/ic_search_light_grey.xml | 4 +- app/src/main/res/drawable/ic_select_all.xml | 13 +- app/src/main/res/drawable/ic_select_none.xml | 13 +- app/src/main/res/drawable/ic_send.xml | 4 +- app/src/main/res/drawable/ic_settings.xml | 4 +- app/src/main/res/drawable/ic_share.xml | 10 +- app/src/main/res/drawable/ic_sync.xml | 11 +- app/src/main/res/drawable/ic_synced.xml | 2 +- .../main/res/drawable/ic_synchronizing.xml | 2 +- .../res/drawable/ic_synchronizing_error.xml | 2 +- app/src/main/res/drawable/ic_tag.xml | 11 +- app/src/main/res/drawable/ic_talk.xml | 2 +- app/src/main/res/drawable/ic_twitter.xml | 13 +- app/src/main/res/drawable/ic_unknown.xml | 10 +- app/src/main/res/drawable/ic_unshared.xml | 14 +- app/src/main/res/drawable/ic_user.xml | 12 +- app/src/main/res/drawable/ic_user_outline.xml | 16 + .../main/res/drawable/ic_user_status_away.xml | 16 +- .../main/res/drawable/ic_user_status_busy.xml | 18 + .../main/res/drawable/ic_user_status_dnd.xml | 22 +- .../res/drawable/ic_user_status_invisible.xml | 16 +- .../res/drawable/ic_user_status_online.xml | 18 + app/src/main/res/drawable/ic_view_list.xml | 15 +- app/src/main/res/drawable/ic_view_module.xml | 12 +- app/src/main/res/drawable/ic_warning.xml | 4 +- app/src/main/res/drawable/iconclose.xml | 16 + app/src/main/res/drawable/image_32dp.xml | 12 +- app/src/main/res/drawable/image_fail.xml | 6 +- .../drawable/indicator_dot_not_selected.xml | 2 +- .../res/drawable/indicator_dot_selected.xml | 2 +- app/src/main/res/drawable/nav_activity.xml | 16 + app/src/main/res/drawable/nav_assistant.xml | 22 + app/src/main/res/drawable/nav_community.xml | 13 +- app/src/main/res/drawable/nav_favorites.xml | 10 +- .../main/res/drawable/nav_notifications.xml | 16 - app/src/main/res/drawable/nav_on_device.xml | 13 +- .../res/drawable/nav_on_device_outline.xml | 16 + app/src/main/res/drawable/nav_photos.xml | 10 +- app/src/main/res/drawable/nav_recently.xml | 6 +- .../res/drawable/nav_recently_outline.xml | 16 + app/src/main/res/drawable/nav_settings.xml | 18 +- .../res/drawable/nav_settings_outline.xml | 16 + app/src/main/res/drawable/nav_shared.xml | 10 +- .../main/res/drawable/nav_synced_folders.xml | 6 +- app/src/main/res/drawable/nav_teams.xml | 16 + .../main/res/drawable/nav_teams_outline.xml | 16 + app/src/main/res/drawable/nav_trashbin.xml | 10 +- app/src/main/res/drawable/nextcloud_logo.xml | 2 +- .../res/drawable/nextcloud_splash_logo.xml | 2 +- app/src/main/res/drawable/no_network.xml | 4 +- .../main/res/drawable/notification_icon.xml | 2 +- app/src/main/res/drawable/online_status.xml | 11 - .../main/res/drawable/outline_image_24.xml | 21 +- app/src/main/res/drawable/photo_pin.xml | 2 +- .../res/drawable/photo_pin_background.xml | 2 +- .../drawable/preview_image_gradient_shape.xml | 2 +- .../preview_markdown_gradient_shape.xml | 2 +- .../drawable/process_dialog_background.xml | 2 +- app/src/main/res/drawable/ripple.xml | 17 - app/src/main/res/drawable/round_bgnd.xml | 2 +- app/src/main/res/drawable/rounded_rect.xml | 2 +- .../main/res/drawable/rounded_rect_8dp.xml | 12 + .../main/res/drawable/selector_activity.xml | 11 + .../main/res/drawable/selector_assistant.xml | 11 + .../main/res/drawable/selector_favorites.xml | 11 + app/src/main/res/drawable/selector_files.xml | 11 + app/src/main/res/drawable/selector_media.xml | 11 + .../main/res/drawable/selector_on_device.xml | 11 + .../main/res/drawable/selector_recently.xml | 11 + .../main/res/drawable/selector_settings.xml | 11 + app/src/main/res/drawable/selector_share.xml | 11 + .../res/drawable/selector_tab_activities.xml | 11 + .../main/res/drawable/selector_tab_share.xml | 11 + app/src/main/res/drawable/selector_teams.xml | 11 + .../main/res/drawable/selector_trashbin.xml | 11 + app/src/main/res/drawable/selector_user.xml | 11 + .../main/res/drawable/shared_via_users.xml | 11 +- app/src/main/res/drawable/spinner_inner.xml | 2 +- .../whats_new_progress_transition.xml | 2 +- app/src/main/res/drawable/white_outline.xml | 2 +- app/src/main/res/drawable/zdc_flash_off.xml | 15 + app/src/main/res/drawable/zdc_flash_on.xml | 15 + .../main/res/drawable/zdc_gallery_icon.xml | 15 + .../main/res/drawable/zdc_magic_wand_icon.xml | 15 + .../main/res/drawable/zdc_rotation_icon.xml | 15 + app/src/main/res/drawable/zdc_tick_icon.xml | 16 + .../main/res/layout-land/account_setup.xml | 16 +- .../launcher_splash_icon_guideline.xml | 15 + app/src/main/res/layout/account_action.xml | 2 +- app/src/main/res/layout/account_item.xml | 5 +- .../res/layout/account_removal_dialog.xml | 4 +- app/src/main/res/layout/account_setup.xml | 17 +- .../main/res/layout/account_setup_webview.xml | 23 +- app/src/main/res/layout/accounts_layout.xml | 2 +- app/src/main/res/layout/activity_compose.xml | 23 +- .../res/layout/activity_document_scan.xml | 4 +- .../main/res/layout/activity_edit_image.xml | 2 +- app/src/main/res/layout/activity_etm.xml | 2 +- .../main/res/layout/activity_list_item.xml | 4 +- .../res/layout/activity_list_item_header.xml | 2 +- .../activity_list_item_header_shimmer.xml | 2 +- .../res/layout/activity_list_item_shimmer.xml | 2 +- .../main/res/layout/activity_list_layout.xml | 3 +- .../main/res/layout/activity_manage_space.xml | 28 +- .../res/layout/activity_preview_bitmap.xml | 2 +- .../res/layout/activity_preview_media.xml | 85 +- app/src/main/res/layout/activity_row.xml | 2 +- .../main/res/layout/activity_show_error.xml | 2 +- app/src/main/res/layout/activity_splash.xml | 9 +- app/src/main/res/layout/backup_fragment.xml | 2 +- app/src/main/res/layout/backup_list_item.xml | 2 +- .../res/layout/backup_list_item_header.xml | 2 +- .../main/res/layout/backuplist_fragment.xml | 2 +- .../res/layout/calendarlist_list_item.xml | 2 +- app/src/main/res/layout/choose_template.xml | 2 +- app/src/main/res/layout/circle_shimmer.xml | 17 + app/src/main/res/layout/community_layout.xml | 3 +- .../res/layout/conflict_resolve_dialog.xml | 34 +- .../main/res/layout/contactlist_list_item.xml | 4 +- .../layout/contactlist_list_item_shimmer.xml | 2 +- .../main/res/layout/contacts_preference.xml | 5 +- app/src/main/res/layout/dashboard_widget.xml | 2 +- .../dashboard_widget_configuration_layout.xml | 2 +- app/src/main/res/layout/deep_link_login.xml | 2 +- .../main/res/layout/dialog_choose_account.xml | 46 +- .../layout/dialog_data_storage_location.xml | 47 + .../main/res/layout/dialog_preview_video.xml | 2 +- .../res/layout/dialog_scan_export_type.xml | 2 +- app/src/main/res/layout/dialog_set_status.xml | 454 - app/src/main/res/layout/dialog_show_tos.xml | 37 + .../layout/dialog_sso_grant_permission.xml | 2 +- .../main/res/layout/document_page_item.xml | 2 +- app/src/main/res/layout/drawer.xml | 3 +- app/src/main/res/layout/drawer_header.xml | 63 +- app/src/main/res/layout/edit_box_dialog.xml | 2 +- app/src/main/res/layout/empty_list.xml | 6 +- .../res/layout/enforced_servers_spinner.xml | 16 + .../layout/etm_background_job_list_item.xml | 2 +- .../res/layout/etm_transfer_list_item.xml | 2 +- .../main/res/layout/externalsite_webview.xml | 3 +- .../res/layout/file_actions_bottom_sheet.xml | 19 +- .../layout/file_actions_bottom_sheet_item.xml | 2 +- .../file_details_activities_fragment.xml | 4 +- .../main/res/layout/file_details_fragment.xml | 39 +- .../res/layout/file_details_share_group.xml | 2 +- ...file_details_share_internal_share_link.xml | 2 +- .../file_details_share_link_share_item.xml | 18 +- ...details_share_public_link_add_new_item.xml | 2 +- ...ls_share_secure_file_drop_add_new_item.xml | 2 +- .../layout/file_details_share_share_item.xml | 4 +- .../layout/file_details_sharing_fragment.xml | 302 +- ...ils_sharing_menu_bottom_sheet_fragment.xml | 36 +- .../file_details_sharing_process_fragment.xml | 278 +- .../layout/file_details_sharing_shimmer.xml | 25 + .../res/layout/file_download_fragment.xml | 2 +- ...file_list_actions_bottom_sheet_creator.xml | 2 +- ...ile_list_actions_bottom_sheet_fragment.xml | 3 +- app/src/main/res/layout/file_thumbnail.xml | 2 +- app/src/main/res/layout/files.xml | 18 +- .../main/res/layout/files_folder_picker.xml | 4 +- app/src/main/res/layout/files_picker.xml | 2 +- .../main/res/layout/first_run_activity.xml | 4 +- .../main/res/layout/fragment_compose_view.xml | 4 +- .../main/res/layout/fragment_etm_accounts.xml | 2 +- .../layout/fragment_etm_background_jobs.xml | 2 +- .../res/layout/fragment_etm_downloader.xml | 2 +- app/src/main/res/layout/fragment_etm_menu.xml | 2 +- .../res/layout/fragment_etm_migrations.xml | 2 +- .../res/layout/fragment_etm_preferences.xml | 2 +- .../layout/fragment_gallery_bottom_sheet.xml | 2 +- .../res/layout/fragment_preview_media.xml | 19 +- app/src/main/res/layout/gallery_header.xml | 2 +- app/src/main/res/layout/gallery_row.xml | 2 +- .../main/res/layout/generic_explanation.xml | 2 +- app/src/main/res/layout/grid_image.xml | 182 - app/src/main/res/layout/grid_item.xml | 345 +- app/src/main/res/layout/grid_sync_item.xml | 2 +- app/src/main/res/layout/info_box.xml | 2 +- .../layout/internal_two_way_sync_layout.xml | 71 + .../internal_two_way_sync_view_holder.xml | 106 + .../layout/item_quick_share_permissions.xml | 52 +- .../layout/launcher_splash_icon_guideline.xml | 15 + app/src/main/res/layout/list_footer.xml | 2 +- app/src/main/res/layout/list_fragment.xml | 2 +- app/src/main/res/layout/list_header.xml | 56 +- .../main/res/layout/list_header_open_in.xml | 69 + app/src/main/res/layout/list_item.xml | 45 +- app/src/main/res/layout/loading_dialog.xml | 4 +- .../main/res/layout/loading_text_shimmer.xml | 19 + .../main/res/layout/log_entry_list_item.xml | 2 +- .../res/layout/login_flow_info_layout_v2.xml | 41 + app/src/main/res/layout/logs_activity.xml | 2 +- .../layout/material_list_item_single_line.xml | 2 +- app/src/main/res/layout/media_control.xml | 67 +- app/src/main/res/layout/multiple_accounts.xml | 2 +- app/src/main/res/layout/note_dialog.xml | 2 +- .../res/layout/notification_list_item.xml | 54 +- .../main/res/layout/notifications_layout.xml | 118 +- app/src/main/res/layout/passcodelock.xml | 2 +- app/src/main/res/layout/password_dialog.xml | 2 +- app/src/main/res/layout/predefined_status.xml | 4 +- .../res/layout/preview_image_activity.xml | 2 +- .../layout/preview_image_details_fragment.xml | 2 +- .../layout/preview_image_error_fragment.xml | 2 +- .../res/layout/preview_image_fragment.xml | 8 +- .../layout/profile_bottom_sheet_action.xml | 2 +- .../layout/profile_bottom_sheet_fragment.xml | 4 +- ...ring_permissions_bottom_sheet_fragment.xml | 2 +- .../res/layout/receive_external_files.xml | 2 +- .../main/res/layout/recommended_file_item.xml | 211 + .../main/res/layout/richdocuments_webview.xml | 3 +- .../res/layout/search_users_groups_layout.xml | 2 +- app/src/main/res/layout/send_button.xml | 2 +- .../main/res/layout/send_files_fragment.xml | 2 +- .../main/res/layout/send_share_fragment.xml | 2 +- .../layout/set_online_status_bottom_sheet.xml | 391 + .../set_status_message_bottom_sheet.xml | 149 + .../res/layout/setup_encryption_dialog.xml | 2 +- app/src/main/res/layout/share_activity.xml | 2 +- .../res/layout/share_list_item_shimmer.xml | 37 + .../res/layout/sorting_order_fragment.xml | 4 +- .../res/layout/ssl_untrusted_cert_layout.xml | 2 +- .../main/res/layout/storage_path_dialog.xml | 2 +- app/src/main/res/layout/storage_path_item.xml | 4 +- .../main/res/layout/synced_folders_empty.xml | 2 +- .../main/res/layout/synced_folders_footer.xml | 2 +- .../res/layout/synced_folders_item_header.xml | 15 +- .../main/res/layout/synced_folders_layout.xml | 12 +- .../synced_folders_list_item_shimmer.xml | 2 +- .../layout/synced_folders_settings_layout.xml | 13 +- app/src/main/res/layout/template_button.xml | 19 +- app/src/main/res/layout/test_layout.xml | 3 +- app/src/main/res/layout/text_file_preview.xml | 2 +- .../main/res/layout/toolbar_back_button.xml | 14 + app/src/main/res/layout/toolbar_standard.xml | 19 +- app/src/main/res/layout/trashbin_activity.xml | 3 +- app/src/main/res/layout/trashbin_item.xml | 6 +- .../main/res/layout/trashbin_item_shimmer.xml | 2 +- .../main/res/layout/unified_search_empty.xml | 2 +- .../main/res/layout/unified_search_footer.xml | 2 +- .../main/res/layout/unified_search_header.xml | 2 +- .../main/res/layout/unified_search_item.xml | 2 +- .../main/res/layout/upload_file_dialog.xml | 2 +- .../main/res/layout/upload_files_layout.xml | 2 +- .../main/res/layout/upload_list_header.xml | 28 +- app/src/main/res/layout/upload_list_item.xml | 2 +- .../main/res/layout/upload_list_layout.xml | 3 +- .../res/layout/uploader_list_item_layout.xml | 2 +- .../layout/user_info_details_table_item.xml | 2 +- app/src/main/res/layout/user_info_layout.xml | 2 +- app/src/main/res/layout/version_list_item.xml | 6 +- .../main/res/layout/whats_new_activity.xml | 4 +- app/src/main/res/layout/whats_new_element.xml | 2 +- .../res/layout/whats_new_webview_element.xml | 2 +- app/src/main/res/layout/widget_item.xml | 4 +- .../main/res/layout/widget_item_load_more.xml | 2 +- app/src/main/res/layout/widget_list_item.xml | 2 +- .../main/res/menu/activity_document_scan.xml | 2 +- .../main/res/menu/activity_file_display.xml | 2 +- .../main/res/menu/activity_folder_picker.xml | 2 +- .../menu/activity_internal_two_way_sync.xml | 17 + app/src/main/res/menu/activity_logs.xml | 2 +- .../main/res/menu/activity_notifications.xml | 2 +- .../menu/activity_receive_external_files.xml | 2 +- app/src/main/res/menu/activity_show_error.xml | 2 +- .../main/res/menu/activity_synced_folders.xml | 2 +- app/src/main/res/menu/activity_trashbin.xml | 2 +- .../main/res/menu/activity_upload_files.xml | 2 +- .../main/res/menu/activity_upload_list.xml | 2 +- .../main/res/menu/bottom_navigation_menu.xml | 33 + .../main/res/menu/custom_menu_placeholder.xml | 2 +- .../main/res/menu/fragment_contact_list.xml | 2 +- .../main/res/menu/fragment_etm_accounts.xml | 4 +- .../res/menu/fragment_etm_background_jobs.xml | 2 +- .../res/menu/fragment_etm_file_transfer.xml | 2 +- .../main/res/menu/fragment_etm_migrations.xml | 2 +- .../res/menu/fragment_etm_preferences.xml | 4 +- .../res/menu/fragment_gallery_three_dots.xml | 2 +- app/src/main/res/menu/item_account.xml | 2 +- app/src/main/res/menu/item_trashbin.xml | 2 +- .../main/res/menu/partial_drawer_entries.xml | 82 +- .../main/res/menu/synced_folders_adapter.xml | 2 +- .../menu/upload_list_cancelled_options.xml | 2 +- .../res/menu/upload_list_failed_options.xml | 2 +- .../menu/upload_list_item_file_conflict.xml | 2 +- .../ic_launcher.xml | 2 +- app/src/main/res/resources.properties | 8 + app/src/main/res/values-ar/strings.xml | 382 +- app/src/main/res/values-ast/strings.xml | 76 +- app/src/main/res/values-b+en+001/strings.xml | 295 +- app/src/main/res/values-bg-rBG/strings.xml | 106 +- app/src/main/res/values-br/strings.xml | 99 +- app/src/main/res/values-ca/strings.xml | 227 +- app/src/main/res/values-cs-rCZ/strings.xml | 349 +- app/src/main/res/values-da/strings.xml | 418 +- app/src/main/res/values-de/strings.xml | 335 +- app/src/main/res/values-el/strings.xml | 108 +- app/src/main/res/values-eo/strings.xml | 47 +- app/src/main/res/values-es-rAR/strings.xml | 88 +- app/src/main/res/values-es-rCL/strings.xml | 51 +- app/src/main/res/values-es-rCO/strings.xml | 219 +- app/src/main/res/values-es-rCR/strings.xml | 540 - app/src/main/res/values-es-rDO/strings.xml | 556 - app/src/main/res/values-es-rEC/strings.xml | 85 +- app/src/main/res/values-es-rGT/strings.xml | 541 - app/src/main/res/values-es-rMX/strings.xml | 101 +- app/src/main/res/values-es-rSV/strings.xml | 541 - app/src/main/res/values-es/strings.xml | 311 +- app/src/main/res/values-et-rEE/strings.xml | 1069 +- app/src/main/res/values-eu/strings.xml | 231 +- app/src/main/res/values-fa/strings.xml | 268 +- app/src/main/res/values-fi-rFI/strings.xml | 192 +- app/src/main/res/values-fr/strings.xml | 322 +- app/src/main/res/values-ga/strings.xml | 324 +- app/src/main/res/values-gd/strings.xml | 49 +- app/src/main/res/values-gl/strings.xml | 389 +- app/src/main/res/values-hr/strings.xml | 74 +- app/src/main/res/values-hu-rHU/strings.xml | 358 +- app/src/main/res/values-in/strings.xml | 374 +- app/src/main/res/values-is/strings.xml | 363 +- app/src/main/res/values-it/strings.xml | 216 +- app/src/main/res/values-iw/strings.xml | 63 +- app/src/main/res/values-ja-rJP/strings.xml | 309 +- app/src/main/res/values-ka-rGE/strings.xml | 506 - app/src/main/res/values-ka/strings.xml | 87 +- app/src/main/res/values-ko/strings.xml | 198 +- app/src/main/res/values-lo/strings.xml | 47 +- app/src/main/res/values-lt-rLT/strings.xml | 205 +- app/src/main/res/values-lv/strings.xml | 508 +- app/src/main/res/values-mk/strings.xml | 108 +- app/src/main/res/values-nb-rNO/strings.xml | 136 +- app/src/main/res/values-night/colors.xml | 5 +- app/src/main/res/values-night/themes.xml | 4 +- app/src/main/res/values-nl/strings.xml | 293 +- app/src/main/res/values-pl/strings.xml | 320 +- app/src/main/res/values-pt-rBR/strings.xml | 535 +- app/src/main/res/values-pt-rPT/strings.xml | 234 +- app/src/main/res/values-ro/strings.xml | 147 +- app/src/main/res/values-ru/strings.xml | 301 +- app/src/main/res/values-sc/strings.xml | 141 +- app/src/main/res/values-sk-rSK/strings.xml | 288 +- app/src/main/res/values-sl/strings.xml | 121 +- app/src/main/res/values-sq/strings.xml | 43 +- app/src/main/res/values-sr-rSP/strings.xml | 50 +- app/src/main/res/values-sr/strings.xml | 288 +- app/src/main/res/values-sv/strings.xml | 286 +- app/src/main/res/values-sw/strings.xml | 1245 + app/src/main/res/values-sw360dp/dims.xml | 2 +- app/src/main/res/values-sw600dp/dims.xml | 2 +- app/src/main/res/values-th-rTH/strings.xml | 53 +- app/src/main/res/values-tk/strings.xml | 48 +- app/src/main/res/values-tr/strings.xml | 335 +- app/src/main/res/values-ug/strings.xml | 1088 + app/src/main/res/values-uk/strings.xml | 324 +- app/src/main/res/values-v27/styles.xml | 42 - app/src/main/res/values-vi/strings.xml | 78 +- app/src/main/res/values-zh-rCN/strings.xml | 405 +- app/src/main/res/values-zh-rHK/strings.xml | 302 +- app/src/main/res/values-zh-rTW/strings.xml | 1021 +- app/src/main/res/values/arrays.xml | 38 - app/src/main/res/values/attrs.xml | 12 +- app/src/main/res/values/bools.xml | 2 +- app/src/main/res/values/colors.xml | 19 +- app/src/main/res/values/dims.xml | 38 +- app/src/main/res/values/ids.xml | 3 +- app/src/main/res/values/setup.xml | 17 +- app/src/main/res/values/strings.xml | 345 +- app/src/main/res/values/styles.xml | 42 +- app/src/main/res/values/themes.xml | 4 +- app/src/main/res/xml/app_config.xml | 70 + app/src/main/res/xml/authenticator.xml | 2 +- app/src/main/res/xml/backup_config.xml | 2 +- app/src/main/res/xml/backup_rules.xml | 4 +- .../main/res/xml/dashboard_widget_info.xml | 4 +- app/src/main/res/xml/exposed_filepaths.xml | 4 +- .../main/res/xml/network_security_config.xml | 4 +- app/src/main/res/xml/preferences.xml | 29 +- .../res/xml/users_and_groups_searchable.xml | 2 +- app/src/qa/AndroidManifest.xml | 2 +- .../appReview/InAppReviewHelperImpl.kt | 5 +- .../client/di/VariantComponentsModule.java | 2 +- .../com/nextcloud/client/di/VariantModule.kt | 8 +- .../com/owncloud/android/utils/PushUtils.java | 2 +- .../owncloud/android/utils/SecurityUtils.java | 2 +- .../res/drawable/ic_launcher_background.xml | 2 +- .../res/drawable/ic_launcher_foreground.xml | 2 +- .../qa/res/mipmap-anydpi-v26/ic_launcher.xml | 2 +- app/src/qa/res/values/setup.xml | 2 +- .../client/di/BuildTypeComponentsModule.java | 2 +- .../android/files/FileLockingHelperTest.kt | 2 +- .../nextcloud/android/utils/ExtensionsTest.kt | 2 +- .../client/core/LocalConnectionTest.kt | 6 +- .../client/core/ManualAsyncRunnerTest.kt | 2 +- .../com/nextcloud/client/core/TaskTest.kt | 2 +- .../client/core/ThreadPoolAsyncRunnerTest.kt | 2 +- .../device/TestPowerManagementService.kt | 2 +- .../nextcloud/client/etm/TestEtmViewModel.kt | 2 +- .../client/jobs/BackgroundJobFactoryTest.kt | 2 +- .../client/jobs/ContentObserverWorkTest.kt | 4 +- .../client/logger/FileLogHandlerTest.kt | 2 +- .../com/nextcloud/client/logger/LevelTest.kt | 2 +- .../nextcloud/client/logger/LogEntryTest.kt | 2 +- .../com/nextcloud/client/logger/LoggerTest.kt | 2 +- .../client/logger/ui/AsyncFilterTest.kt | 2 +- .../client/logger/ui/LogsViewModelTest.kt | 2 +- .../client/media/AudioFocusManagerTest.kt | 37 +- .../nextcloud/client/media/AudioFocusTest.kt | 2 +- .../client/media/PlayerStateMachineTest.kt | 2 +- .../client/mixins/MixinRegistryTest.kt | 2 +- .../client/network/ConnectivityServiceTest.kt | 11 +- .../onboarding/OnboardingServiceTest.kt | 15 +- .../preferences/TestAppPreferences.java | 2 +- .../client/utils/FileSortOrderBySizeTests.kt | 165 + .../client/utils/FileStorageUtilsTest.kt | 149 +- .../utils/FolderSizeCalculationTests.kt | 196 + .../nextcloud/client/utils/NaturalSortTest.kt | 2 +- .../nextcloud/client/utils/OCFileSortTest.kt | 6 +- .../SyncedFolderDisplayItemExtensionsTests.kt | 93 + .../nextcloud/client/utils/ThrottlerTest.kt | 2 +- .../AuthenticatorDataUrlTest.java | 44 +- .../AuthenticatorUrlUtilsTest.java | 2 +- .../authentication/PassCodeManagerTest.kt | 2 +- .../owncloud/android/datamodel/OCFileTest.kt | 2 +- .../owncloud/android/ui/TextDrawableTest.kt | 2 +- .../activities/ActivitiesPresenterTest.java | 28 +- .../RemoteActivitiesRepositoryTest.java | 18 +- .../data/files/RemoteFilesRepositoryTest.java | 2 +- .../activity/SyncedFoldersActivityTest.java | 4 +- .../ui/adapter/ActivityListAdapterTest.java | 2 +- .../android/ui/adapter/GalleryAdapterTest.kt | 2 +- .../adapter/OCShareToOCFileConverterTest.kt | 2 +- .../ui/adapter/ShareeListAdapterTest.kt | 32 +- .../ui/adapter/UserListAdapterTest.java | 2 +- .../android/ui/db/OCUploadComparatorTest.kt | 15 +- .../android/utils/DisplayUtilsTest.java | 2 +- .../android/utils/FilesSyncHelperTest.java | 76 + .../android/utils/OwnCloudSessionTest.java | 2 +- .../owncloud/android/utils/PathUtilsTest.kt | 2 +- .../android/utils/StringUtilsTest.java | 2 +- .../appReview/InAppReviewHelperImpl.kt | 5 +- .../client/di/VariantComponentsModule.java | 2 +- .../com/nextcloud/client/di/VariantModule.kt | 6 +- .../com/owncloud/android/utils/PushUtils.java | 2 +- .../owncloud/android/utils/SecurityUtils.java | 2 +- .../drawable-v26/ic_launcher_background.xml | 2 +- .../drawable-v26/ic_launcher_foreground.xml | 2 +- .../res/drawable/ic_launcher_foreground.xml | 2 +- .../res/mipmap-anydpi-v26/ic_launcher.xml | 2 +- app/src/versionDev/res/values/setup.xml | 2 +- appscan/.gitignore | 3 - appscan/build.gradle | 27 +- appscan/src/main/AndroidManifest.xml | 2 +- .../com/nextcloud/appscan/AppScanActivity.kt | 5 +- .../com/nextcloud/appscan/ScanPageContract.kt | 2 +- build.gradle | 44 +- .../at-1763318315735/css/uikit.min.css | 1 - .../dependency-verification-report.html | 149 - .../at-1763318315735/img/gradle-logo.png | Bin 10776 -> 0 bytes .../at-1763318315735/js/uikit-icons.min.js | 3 - .../at-1763318315735/js/uikit.min.js | 3 - doc/Nextcloud_Android_Screenshots.png.license | 2 +- doc/branching.png.license | 2 +- doc/branching.svg.license | 2 +- drawable_resources/audio.svg | 1 - drawable_resources/audio.svg.license | 2 - drawable_resources/dashboard.svg | 15 - drawable_resources/dashboard.svg.license | 2 - .../first_run_groupware.svg.license | 2 +- drawable_resources/first_run_talk.svg.license | 2 +- drawable_resources/ic_activity.svg | 58 - drawable_resources/ic_activity.svg.license | 2 - drawable_resources/ic_activity_light_grey.svg | 58 - .../ic_activity_light_grey.svg.license | 2 - drawable_resources/ic_decrypt.svg | 5 + drawable_resources/ic_decrypt.svg.license | 2 + drawable_resources/ic_encrypt.svg | 5 + drawable_resources/ic_encrypt.svg.license | 2 + drawable_resources/ic_file.svg | 5 - drawable_resources/ic_file.svg.license | 3 - drawable_resources/ic_home.svg | 7 - drawable_resources/ic_home.svg.license | 2 - drawable_resources/ic_list_empty_home.svg | 62 - .../ic_list_empty_home.svg.license | 2 - drawable_resources/ic_locked_dots_small.svg | 8 + .../ic_locked_dots_small.svg.license | 3 + drawable_resources/ic_more_apps.svg | 16 + drawable_resources/ic_more_apps.svg.license | 2 + .../icon-background.svg.license | 2 +- drawable_resources/logo.svg.license | 2 +- drawable_resources/logo_dev.svg.license | 2 +- drawable_resources/logo_qa.svg.license | 2 +- drawable_resources/nextcloud-logo.svg.license | 2 +- .../nextcloud-splash-logo.svg.license | 2 +- .../notification-icon.svg.license | 2 +- .../package-x-generic.svg.license | 2 +- drawable_resources/qrcode.svg.license | 2 +- drawable_resources/shared_via_link.svg | 36 - .../shared_via_link.svg.license | 2 - drawable_resources/spinner_inner.svg.license | 2 +- drawable_resources/state-error.svg.license | 2 +- drawable_resources/state-offline.svg.license | 2 +- drawable_resources/state-ok.svg.license | 2 +- drawable_resources/state-pause.svg.license | 2 +- drawable_resources/state-sync.svg.license | 2 +- drawable_resources/state-warning.svg.license | 2 +- .../user-status-away.svg.license | 2 +- .../user-status-dnd.svg.license | 2 +- .../user-status-invisible.svg.license | 2 +- drawable_resources/view_play.svg.license | 2 +- .../whats_new_accounts.svg.license | 2 +- .../whats_new_auto_upload.svg.license | 2 +- .../whats_new_device_credentials.svg.license | 2 +- ...hats_new_end_to_end_encryption.svg.license | 2 +- .../whats_new_files.svg.license | 2 +- .../whats_new_fingerprint.svg.license | 2 +- drawable_resources/whats_new_ipv6.svg.license | 2 +- .../whats_new_notifications.svg.license | 2 +- .../whats_new_resized_images.svg.license | 2 +- .../whats_new_search.svg.license | 2 +- .../whats_new_theming.svg.license | 2 +- fastlane/Appfile.license | 2 +- fastlane/CHANGELOG.md | 4 - fastlane/Fastfile | 2 +- fastlane/Pluginfile.license | 2 +- fastlane/Screengrabfile | 2 +- .../android/en-US/changelogs/30290051.txt | 18 - .../android/en-US/changelogs/30290090.txt | 18 - .../android/en-US/changelogs/30290151.txt | 7 - .../en-US/changelogs/30290151.txt.license | 2 - .../android/en-US/changelogs/30290190.txt | 7 - .../en-US/changelogs/30290190.txt.license | 2 - .../android/en-US/changelogs/30290290.txt | 7 - .../en-US/changelogs/30290290.txt.license | 2 - .../android/en-US/changelogs/30290351.txt | 7 - .../en-US/changelogs/30290351.txt.license | 2 - .../android/en-US/changelogs/30290390.txt | 7 - .../en-US/changelogs/30290390.txt.license | 2 - .../android/en-US/changelogs/30340051.txt | 9 + .../en-US/changelogs/30340051.txt.license | 2 + .../android/en-US/changelogs/30340052.txt | 9 + .../en-US/changelogs/30340052.txt.license | 2 + .../android/en-US/changelogs/30340090.txt | 7 + .../en-US/changelogs/30340090.txt.license | 2 + .../android/en-US/changelogs/30340151.txt | 9 + .../en-US/changelogs/30340151.txt.license | 2 + .../android/en-US/changelogs/30340190.txt | 9 + .../en-US/changelogs/30340190.txt.license | 2 + gradle.properties | 15 +- gradle/libs.versions.toml | 221 + gradle/verification-keyring.keys | 1941 +- gradle/verification-keyring.keys.license | 2 +- gradle/verification-metadata.xml | 20784 +++++++++++++++- gradle/verification-metadata.xml.license | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- .../wrapper/gradle-wrapper.properties.license | 2 +- gradlew | 14 +- gradlew.bat | 6 +- jacoco.gradle | 113 +- local.properties | 8 - ndk.env.license | 2 +- renovate.json5 | 24 +- renovate.json5.license | 2 +- ruleset.xml | 2 +- scripts/QA_keystore.jks | Bin 2236 -> 0 bytes scripts/QA_keystore.jks.license | 2 - scripts/analysis/analysis-wrapper.sh | 20 +- scripts/analysis/detectWrongSettings.sh | 24 +- scripts/analysis/getBranchBase.sh | 2 +- scripts/analysis/getBranchName.sh | 6 +- scripts/analysis/lint-results.txt | 2 +- scripts/analysis/lint-results.txt.license | 2 +- scripts/analysis/lint-up.rb | 4 +- scripts/analysis/spotbugs-filter.xml | 3 +- scripts/analysis/spotbugs-up.rb | 2 +- scripts/analysis/spotbugsComparison.py | 2 +- scripts/analysis/spotbugsSummary.py | 2 +- scripts/androidScreenshotTest | 4 +- scripts/buildDev | 2 +- scripts/checkGplayLimitation.sh | 2 +- scripts/checkIfRunDrone.sh | 2 +- scripts/deleteOldComments.sh | 2 +- scripts/generateScreenshotOverview.sh | 2 +- scripts/hooks/prepare-commit-msg | 26 + scripts/lib.sh | 2 +- scripts/metadata/generate_metadata.py | 2 +- scripts/repo | 1 + scripts/repo.license | 2 + scripts/runAllScreenshotCombinations | 2 +- scripts/runCombinedTest.sh | 6 +- scripts/screenshotCombinations.license | 2 +- scripts/screenshotSummary.sh | 2 +- scripts/screenshots/addMockDevice.sh | 2 +- scripts/screenshots/generateScreenshotHtml.sh | 2 +- scripts/screenshots/phone.svg.license | 2 +- scripts/screenshots/sevenInch.svg.license | 2 +- scripts/updateLibraryHash.sh | 16 +- scripts/updateScreenshots.sh | 2 +- scripts/uploadArtifact.sh | 41 - scripts/uploadReport.sh | 13 +- scripts/uploadScreenshotSummary.sh | 2 +- scripts/wait_for_server.sh | 2 +- settings.gradle | 16 +- src/README.md | 2 +- .../android/da-DK/full_description.txt | 25 +- .../android/de-DE/full_description.txt | 12 +- .../android/el-GR/full_description.txt | 30 +- .../android/en-US/full_description.txt | 2 +- .../android/es-ES/full_description.txt | 4 +- .../android/es-ES/short_description.txt | 2 +- .../android/eu-ES/full_description.txt | 27 +- .../android/fr-FR/full_description.txt | 28 +- .../android/is-IS/full_description.txt | 28 +- .../android/is-IS/short_description.txt | 2 +- .../android/it-IT/full_description.txt | 25 +- .../android/nl-NL/full_description.txt | 24 +- .../android/pl-PL/full_description.txt | 24 +- .../android/pt-BR/full_description.txt | 8 +- .../android/sr-SR/full_description.txt | 2 +- .../android/sv-SE/full_description.txt | 17 +- .../android/tr-TR/full_description.txt | 2 +- .../android/uk-UK/full_description.txt | 27 +- .../android/es-ES/full_description.txt | 2 +- .../android/es-ES/short_description.txt | 2 +- .../android/fa-FA/full_description.txt | 2 +- .../android/is-IS/full_description.txt | 2 +- .../android/is-IS/short_description.txt | 2 +- suppressions.xml | 2 +- 2340 files changed, 100899 insertions(+), 37798 deletions(-) create mode 100644 LICENSES/LicenseRef-XTrademarks.txt create mode 100644 REUSE.toml create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/82.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/83.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/84.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/85.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/87.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/88.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/89.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/90.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/91.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/92.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/93.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/94.json create mode 100644 app/schemas/com.nextcloud.client.database.NextcloudDatabase/95.json rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_empty.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_empty_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_error.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_error_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_loading.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.AuthenticatorActivityIT_login.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.CommunityActivityIT_open_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.CommunityActivityIT_open_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.CommunityActivityIT_open_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.CommunityActivityIT_open_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.CommunityActivityIT_open_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityIT_shareToCircle.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityIT_showAccounts.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityIT_showShares.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_open.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_open_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_open_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_open_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_open_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_open_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_showMediaThenAllFiles.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FirstRunActivityIT_open.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FirstRunActivityIT_open_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FirstRunActivityIT_open_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FirstRunActivityIT_open_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FirstRunActivityIT_open_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.FirstRunActivityIT_open_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.SettingsActivityIT_open.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_open_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_open_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_open_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_open_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_open_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_showPowerCheckDialog.png create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.etm.EtmActivityTest_accounts.png create mode 100644 app/screenshots/generic/debug/com.nextcloud.client.etm.EtmActivityTest_overview.png rename app/screenshots/{gplay => generic}/debug/com.nextcloud.ui.BitmapIT_glideSVG.png (100%) rename app/screenshots/{gplay => generic}/debug/com.nextcloud.ui.BitmapIT_roundBitmap.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testChooseLocationAction.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testMoveOrCopy.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_empty.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_empty_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_empty_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_empty_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_empty_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_empty_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_error.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_error_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_error_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_error_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_error_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_error_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.NotificationsActivityIT_showNotifications_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_check.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_check_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_check_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_check_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_check_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_check_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_delete.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_delete_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_delete_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_delete_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_delete_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_delete_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_request.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_request_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_request_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_request_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_request_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.PassCodeActivityIT_request_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivityIT_open.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivityIT_openMultiAccount.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_localFolderPickerMode.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_open.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_search.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_selectAll.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dark_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithOneAction.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeAction.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeActionRTL.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithTwoAction.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testEnforcedPasswordDialog.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testFileActionsBottomSheet.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testOptionalPasswordDialog.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testProfileBottomSheet.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testSslUntrustedCertDialog.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testStoragePermissionDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testAccountChooserDialog.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testLoadingDialog.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testNewFolderDialog.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFileDialog.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFilesDialog.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFolderDialog.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFoldersDialog.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRenameFileDialog.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialogDifferentTypes_Screenshot.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_Screenshot.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFile.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFolder.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarAndContactsList.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarList.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showContactList.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showLoading.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Activities.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Sharing.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailDetailsFragment.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showEmpty.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showGallery.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.GroupfolderListFragmentIT_showEmpty.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GroupfolderListFragmentIT_showGroupfolder.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GroupfolderListFragmentIT_showGroupfolders.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFolderTypes.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showOneFile.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles_light_white.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.fragment.SharedListFragmentIT_showSharedFiles.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.preview.PreviewBitmapScreenshotIT_showBitmap.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage_dark_blue.png (100%) create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png create mode 100644 app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.preview.pdf.PreviewPdfFragmentScreenshotIT_showPdf.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_differentUser.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_blue.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_black.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_white.png (100%) rename app/screenshots/{gplay => generic}/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_normalUser.png (100%) rename app/screenshots/{gplay => generic}/debug/richworkspaces_dark.png (100%) rename app/screenshots/{gplay => generic}/debug/richworkspaces_light.png (100%) delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.etm.EtmActivityTest_accounts.png delete mode 100644 app/screenshots/gplay/debug/com.nextcloud.client.etm.EtmActivityTest_overview.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testChooseLocationAction.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testMoveOrCopy.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivityIT_open.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivityIT_openMultiAccount.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_localFolderPickerMode.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithOneAction.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeAction.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeActionRTL.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithTwoAction.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testEnforcedPasswordDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testFileActionsBottomSheet.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testOptionalPasswordDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testProfileBottomSheet.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testSslUntrustedCertDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testStoragePermissionDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialogDifferentTypes_Screenshot.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_Screenshot.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarAndContactsList.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarList.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showContactList.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showLoading.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showGallery.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.GroupfolderListFragmentIT_showGroupfolder.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.GroupfolderListFragmentIT_showGroupfolders.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFolderTypes.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.SharedListFragmentIT_showSharedFiles.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png delete mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png create mode 100644 app/src/androidTest/assets/credentials.json delete mode 100644 app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.java create mode 100644 app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.kt delete mode 100644 app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.java create mode 100644 app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.kt delete mode 100644 app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java create mode 100644 app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt delete mode 100644 app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.java create mode 100644 app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.kt create mode 100644 app/src/androidTest/java/com/nextcloud/extensions/BitmapRotationTests.kt create mode 100644 app/src/androidTest/java/com/nextcloud/extensions/GetExifOrientationTests.kt create mode 100644 app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt create mode 100644 app/src/androidTest/java/com/nextcloud/ui/SetOnlineStatusBottomSheetIT.kt delete mode 100644 app/src/androidTest/java/com/nextcloud/ui/SetStatusDialogFragmentIT.kt create mode 100644 app/src/androidTest/java/com/nextcloud/ui/SetStatusMessageBottomSheetIT.kt create mode 100644 app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt create mode 100644 app/src/androidTest/java/com/nextcloud/utils/CertificateValidatorTests.kt create mode 100644 app/src/androidTest/java/com/nextcloud/utils/CheckWCFRestrictionsTests.kt create mode 100644 app/src/androidTest/java/com/nextcloud/utils/FileHelperTest.kt create mode 100644 app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt create mode 100644 app/src/androidTest/java/com/nextcloud/utils/SharePermissionManagerTest.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.kt create mode 100644 app/src/androidTest/java/com/owncloud/android/datamodel/Credentials.kt create mode 100644 app/src/androidTest/java/com/owncloud/android/extensions/AbstractITExtensions.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/files/services/LegacyFileUploaderIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewImageFragmentIT.kt delete mode 100644 app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java create mode 100644 app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.kt create mode 100644 app/src/main/java/com/nextcloud/client/assistant/model/ScreenOverlayState.kt create mode 100644 app/src/main/java/com/nextcloud/client/assistant/model/ScreenState.kt delete mode 100644 app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt create mode 100644 app/src/main/java/com/nextcloud/client/assistant/task/TaskStatusView.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/dao/OfflineOperationDao.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/dao/RecommendedFileDao.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/entity/OfflineOperationEntity.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/migrations/Migration88to89.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/migrations/model/SQLiteColumnType.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/typeAdapter/OfflineOperationTypeAdapter.kt create mode 100644 app/src/main/java/com/nextcloud/client/database/typeConverter/OfflineOperationTypeConverter.kt create mode 100644 app/src/main/java/com/nextcloud/client/files/DeepLinkConstants.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/clipboard/ClipboardClearWorker.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsNotificationManager.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/offlineOperations/receiver/OfflineOperationReceiver.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepository.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepositoryType.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/operation/FileOperationHelper.kt create mode 100644 app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadBroadcastReceiver.kt create mode 100644 app/src/main/java/com/nextcloud/client/media/BackgroundPlayerService.kt create mode 100644 app/src/main/java/com/nextcloud/model/OCFileFilterType.kt create mode 100644 app/src/main/java/com/nextcloud/model/OfflineOperationType.kt create mode 100644 app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt create mode 100644 app/src/main/java/com/nextcloud/model/ShareeEntry.kt create mode 100644 app/src/main/java/com/nextcloud/model/ToolbarItem.kt create mode 100644 app/src/main/java/com/nextcloud/receiver/NetworkChangeReceiver.kt create mode 100644 app/src/main/java/com/nextcloud/repository/ClientRepository.kt create mode 100644 app/src/main/java/com/nextcloud/repository/RemoteClientRepository.kt create mode 100644 app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt create mode 100644 app/src/main/java/com/nextcloud/ui/RetrieveStatus.kt create mode 100644 app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt rename app/src/main/java/com/nextcloud/ui/{SetStatusDialogFragment.kt => SetStatusMessageBottomSheet.kt} (69%) create mode 100644 app/src/main/java/com/nextcloud/ui/behavior/OnScrollBehavior.kt create mode 100644 app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileAction.kt create mode 100644 app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt create mode 100644 app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsViewModel.kt create mode 100644 app/src/main/java/com/nextcloud/utils/BitmapExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/BuildHelper.kt create mode 100644 app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt create mode 100644 app/src/main/java/com/nextcloud/utils/ContactManager.kt create mode 100644 app/src/main/java/com/nextcloud/utils/FileHelper.kt create mode 100644 app/src/main/java/com/nextcloud/utils/GlideHelper.kt create mode 100644 app/src/main/java/com/nextcloud/utils/LinkHelper.kt create mode 100644 app/src/main/java/com/nextcloud/utils/OCFileUtils.kt create mode 100644 app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt create mode 100644 app/src/main/java/com/nextcloud/utils/date/DateFormatPattern.kt create mode 100644 app/src/main/java/com/nextcloud/utils/date/DateFormatter.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/ActionBarExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/DateExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/DecryptedUserExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/DrawableExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/FragmentExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/IntExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/OCCapabilityExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/OCUploadExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/OnDataTransferProgressListenerExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/ParcableExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/ShareTypeExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/ThumbnailsCacheManagerExtensions.kt create mode 100644 app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt create mode 100644 app/src/main/java/com/nextcloud/utils/mdm/MDMConfig.kt create mode 100644 app/src/main/java/com/nextcloud/utils/numberFormatter/NumberFormatter.kt create mode 100644 app/src/main/java/com/owncloud/android/authentication/AuthObject.kt create mode 100644 app/src/main/java/com/owncloud/android/authentication/EnforcedServer.kt delete mode 100644 app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.java create mode 100644 app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.kt delete mode 100644 app/src/main/java/com/owncloud/android/datamodel/QuickPermissionModel.kt create mode 100644 app/src/main/java/com/owncloud/android/datamodel/SharesType.kt create mode 100644 app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermission.kt create mode 100644 app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt delete mode 100644 app/src/main/java/com/owncloud/android/media/MediaControlView.java create mode 100644 app/src/main/java/com/owncloud/android/media/MediaControlView.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt delete mode 100644 app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java create mode 100644 app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt delete mode 100644 app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java create mode 100644 app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiver.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiverActions.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/upload/UploadFileException.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/upload/UploadFileOperationExtensions.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.java create mode 100644 app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/CompletionCallback.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/ListPreferenceDialog.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt rename app/src/main/java/com/owncloud/android/ui/adapter/{GroupfolderListAdapter.kt => GroupFolderListAdapter.kt} (70%) create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncAdapter.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt rename app/src/main/java/com/owncloud/android/ui/adapter/{ListGridImageViewHolder.kt => ListViewHolder.kt} (91%) create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/OCFileListRecommendedItemViewHolder.kt rename app/src/main/java/com/owncloud/android/ui/adapter/{OCFileListGridImageViewHolder.kt => OCFileListViewHolder.kt} (75%) delete mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/ProgressListener.java create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/ViewType.java create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/progressListener/DownloadProgressListener.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/progressListener/ProgressListener.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/progressListener/UploadProgressListener.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/asynctasks/RetrieveStatusAsyncTask.java delete mode 100644 app/src/main/java/com/owncloud/android/ui/components/CustomViewPager.java delete mode 100644 app/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java delete mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/RenamePublicShareDialogFragment.java delete mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/ShareLinkToDialog.java create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/ShareLinkToDialog.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/TermsOfServiceDialog.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/parcel/ConflictDialogData.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/setupEncryption/CertificateValidator.kt rename app/src/main/java/com/owncloud/android/ui/dialog/{ => setupEncryption}/SetupEncryptionDialogFragment.kt (69%) create mode 100644 app/src/main/java/com/owncloud/android/ui/events/DialogEvent.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/events/EventBusFactory.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/events/FileDownloadProgressEvent.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchAsyncTask.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchTask.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/filesRepository/FilesRepository.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/filesRepository/RemoteFilesRepository.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/share/RemoteShareRepository.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/share/ShareRepository.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/util/SharePermissionManager.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/util/SharingMenuHelper.java create mode 100644 app/src/main/java/com/owncloud/android/ui/interfaces/TransactionInterface.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java create mode 100644 app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java create mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewImageErrorFragment.java create mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewImageErrorFragment.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java create mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java create mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.kt delete mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java create mode 100644 app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/crypto/CryptoError.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/crypto/CryptoHelper.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/crypto/CryptoStringUtils.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/glide/CustomGlideStreamLoader.java delete mode 100644 app/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.java create mode 100644 app/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/glide/NextcloudGlideModule.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/glide/StringModelLoaderFactory.kt create mode 100644 app/src/main/java/com/owncloud/android/utils/glide/UriModelLoaderFactory.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/MenuSimpleTarget.java delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SVGorImage.java create mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SVGorImage.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgBitmapTranscoder.java create mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgBitmapTranscoder.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.java create mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.java create mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgOrImageBitmapTranscoder.java create mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgOrImageBitmapTranscoder.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgOrImageDecoder.java create mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgOrImageDecoder.kt delete mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgSoftwareLayerSetter.java create mode 100644 app/src/main/java/com/owncloud/android/utils/svg/SvgSoftwareLayerSetter.kt create mode 100644 app/src/main/res/anim/blink.xml create mode 100644 app/src/main/res/color/card_border_selector.xml create mode 100644 app/src/main/res/drawable/chat_bubble.xml create mode 100644 app/src/main/res/drawable/file_whiteboard.xml create mode 100644 app/src/main/res/drawable/ic_arrow_back_foreground.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_check_24.xml create mode 100644 app/src/main/res/drawable/ic_cloud_sync.xml create mode 100644 app/src/main/res/drawable/ic_custom_permissions.xml create mode 100644 app/src/main/res/drawable/ic_expand_less.xml create mode 100644 app/src/main/res/drawable/ic_eye.xml create mode 100644 app/src/main/res/drawable/ic_fast_forward.xml create mode 100644 app/src/main/res/drawable/ic_fast_rewind.xml create mode 100644 app/src/main/res/drawable/ic_file_request.xml create mode 100644 app/src/main/res/drawable/ic_find_in_page.xml create mode 100644 app/src/main/res/drawable/ic_folder_offline.xml create mode 100644 app/src/main/res/drawable/ic_no_internet.xml create mode 100644 app/src/main/res/drawable/ic_pause.xml create mode 100644 app/src/main/res/drawable/ic_play.xml create mode 100644 app/src/main/res/drawable/ic_retry.xml create mode 100644 app/src/main/res/drawable/ic_user_outline.xml create mode 100644 app/src/main/res/drawable/ic_user_status_busy.xml create mode 100644 app/src/main/res/drawable/ic_user_status_online.xml create mode 100644 app/src/main/res/drawable/iconclose.xml create mode 100644 app/src/main/res/drawable/nav_activity.xml create mode 100644 app/src/main/res/drawable/nav_assistant.xml delete mode 100644 app/src/main/res/drawable/nav_notifications.xml create mode 100644 app/src/main/res/drawable/nav_on_device_outline.xml create mode 100644 app/src/main/res/drawable/nav_recently_outline.xml create mode 100644 app/src/main/res/drawable/nav_settings_outline.xml create mode 100644 app/src/main/res/drawable/nav_teams.xml create mode 100644 app/src/main/res/drawable/nav_teams_outline.xml delete mode 100644 app/src/main/res/drawable/online_status.xml delete mode 100644 app/src/main/res/drawable/ripple.xml create mode 100644 app/src/main/res/drawable/rounded_rect_8dp.xml create mode 100644 app/src/main/res/drawable/selector_activity.xml create mode 100644 app/src/main/res/drawable/selector_assistant.xml create mode 100644 app/src/main/res/drawable/selector_favorites.xml create mode 100644 app/src/main/res/drawable/selector_files.xml create mode 100644 app/src/main/res/drawable/selector_media.xml create mode 100644 app/src/main/res/drawable/selector_on_device.xml create mode 100644 app/src/main/res/drawable/selector_recently.xml create mode 100644 app/src/main/res/drawable/selector_settings.xml create mode 100644 app/src/main/res/drawable/selector_share.xml create mode 100644 app/src/main/res/drawable/selector_tab_activities.xml create mode 100644 app/src/main/res/drawable/selector_tab_share.xml create mode 100644 app/src/main/res/drawable/selector_teams.xml create mode 100644 app/src/main/res/drawable/selector_trashbin.xml create mode 100644 app/src/main/res/drawable/selector_user.xml create mode 100644 app/src/main/res/drawable/zdc_flash_off.xml create mode 100644 app/src/main/res/drawable/zdc_flash_on.xml create mode 100644 app/src/main/res/drawable/zdc_gallery_icon.xml create mode 100644 app/src/main/res/drawable/zdc_magic_wand_icon.xml create mode 100644 app/src/main/res/drawable/zdc_rotation_icon.xml create mode 100644 app/src/main/res/drawable/zdc_tick_icon.xml create mode 100644 app/src/main/res/layout-land/launcher_splash_icon_guideline.xml create mode 100644 app/src/main/res/layout/circle_shimmer.xml create mode 100644 app/src/main/res/layout/dialog_data_storage_location.xml delete mode 100644 app/src/main/res/layout/dialog_set_status.xml create mode 100644 app/src/main/res/layout/dialog_show_tos.xml create mode 100644 app/src/main/res/layout/enforced_servers_spinner.xml create mode 100644 app/src/main/res/layout/file_details_sharing_shimmer.xml delete mode 100644 app/src/main/res/layout/grid_image.xml create mode 100644 app/src/main/res/layout/internal_two_way_sync_layout.xml create mode 100644 app/src/main/res/layout/internal_two_way_sync_view_holder.xml create mode 100644 app/src/main/res/layout/launcher_splash_icon_guideline.xml create mode 100644 app/src/main/res/layout/list_header_open_in.xml create mode 100644 app/src/main/res/layout/loading_text_shimmer.xml create mode 100644 app/src/main/res/layout/login_flow_info_layout_v2.xml create mode 100644 app/src/main/res/layout/recommended_file_item.xml create mode 100644 app/src/main/res/layout/set_online_status_bottom_sheet.xml create mode 100644 app/src/main/res/layout/set_status_message_bottom_sheet.xml create mode 100644 app/src/main/res/layout/share_list_item_shimmer.xml create mode 100644 app/src/main/res/layout/toolbar_back_button.xml create mode 100644 app/src/main/res/menu/activity_internal_two_way_sync.xml create mode 100644 app/src/main/res/menu/bottom_navigation_menu.xml rename app/src/main/res/{mipmap-anydpi-v26 => mipmap-anydpi}/ic_launcher.xml (89%) create mode 100644 app/src/main/res/resources.properties delete mode 100644 app/src/main/res/values-es-rCR/strings.xml delete mode 100644 app/src/main/res/values-es-rDO/strings.xml delete mode 100644 app/src/main/res/values-es-rGT/strings.xml delete mode 100644 app/src/main/res/values-es-rSV/strings.xml delete mode 100644 app/src/main/res/values-ka-rGE/strings.xml create mode 100644 app/src/main/res/values-sw/strings.xml create mode 100644 app/src/main/res/values-ug/strings.xml delete mode 100644 app/src/main/res/values-v27/styles.xml delete mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/xml/app_config.xml create mode 100644 app/src/test/java/com/nextcloud/client/utils/FileSortOrderBySizeTests.kt create mode 100644 app/src/test/java/com/nextcloud/client/utils/FolderSizeCalculationTests.kt create mode 100644 app/src/test/java/com/nextcloud/client/utils/SyncedFolderDisplayItemExtensionsTests.kt create mode 100644 app/src/test/java/com/owncloud/android/utils/FilesSyncHelperTest.java delete mode 100644 appscan/.gitignore delete mode 100644 build/reports/dependency-verification/at-1763318315735/css/uikit.min.css delete mode 100644 build/reports/dependency-verification/at-1763318315735/dependency-verification-report.html delete mode 100644 build/reports/dependency-verification/at-1763318315735/img/gradle-logo.png delete mode 100644 build/reports/dependency-verification/at-1763318315735/js/uikit-icons.min.js delete mode 100644 build/reports/dependency-verification/at-1763318315735/js/uikit.min.js delete mode 100644 drawable_resources/audio.svg delete mode 100644 drawable_resources/audio.svg.license delete mode 100644 drawable_resources/dashboard.svg delete mode 100644 drawable_resources/dashboard.svg.license delete mode 100644 drawable_resources/ic_activity.svg delete mode 100644 drawable_resources/ic_activity.svg.license delete mode 100644 drawable_resources/ic_activity_light_grey.svg delete mode 100644 drawable_resources/ic_activity_light_grey.svg.license create mode 100644 drawable_resources/ic_decrypt.svg create mode 100644 drawable_resources/ic_decrypt.svg.license create mode 100644 drawable_resources/ic_encrypt.svg create mode 100644 drawable_resources/ic_encrypt.svg.license delete mode 100644 drawable_resources/ic_file.svg delete mode 100644 drawable_resources/ic_file.svg.license delete mode 100644 drawable_resources/ic_home.svg delete mode 100644 drawable_resources/ic_home.svg.license delete mode 100644 drawable_resources/ic_list_empty_home.svg delete mode 100644 drawable_resources/ic_list_empty_home.svg.license create mode 100644 drawable_resources/ic_locked_dots_small.svg create mode 100644 drawable_resources/ic_locked_dots_small.svg.license create mode 100644 drawable_resources/ic_more_apps.svg create mode 100644 drawable_resources/ic_more_apps.svg.license delete mode 100644 drawable_resources/shared_via_link.svg delete mode 100644 drawable_resources/shared_via_link.svg.license delete mode 100644 fastlane/CHANGELOG.md delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290051.txt delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290090.txt delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290151.txt delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290151.txt.license delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290190.txt delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290190.txt.license delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290290.txt delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290290.txt.license delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290351.txt delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290351.txt.license delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290390.txt delete mode 100644 fastlane/metadata/android/en-US/changelogs/30290390.txt.license create mode 100644 fastlane/metadata/android/en-US/changelogs/30340051.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/30340051.txt.license create mode 100644 fastlane/metadata/android/en-US/changelogs/30340052.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/30340052.txt.license create mode 100644 fastlane/metadata/android/en-US/changelogs/30340090.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/30340090.txt.license create mode 100644 fastlane/metadata/android/en-US/changelogs/30340151.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/30340151.txt.license create mode 100644 fastlane/metadata/android/en-US/changelogs/30340190.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/30340190.txt.license create mode 100644 gradle/libs.versions.toml delete mode 100644 local.properties delete mode 100644 scripts/QA_keystore.jks delete mode 100644 scripts/QA_keystore.jks.license create mode 100755 scripts/hooks/prepare-commit-msg create mode 100644 scripts/repo create mode 100644 scripts/repo.license delete mode 100755 scripts/uploadArtifact.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 97df73b..144f074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,65 @@ +## 3.30.7 (January 6, 2025) + +- Fix crash of auto upload settings + +Minimum: NC 16 Server, Android 7.0 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/104 + +## 3.30.3 (October 22, 2024) + +- Bugfix for two way sync: sync only on wifi + +## 3.30.2 (October 21, 2024) + +- Bugfix for two way sync. Please check listed folders in settings -> internal two way sync + +Minimum: NC 16 Server, Android 7.0 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/99 + +## 3.30.1 (October 11, 2024) + +- Bugfixes + +Minimum: NC 16 Server, Android 7.0 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/99 + +## 3.29.1 (June 27, 2024) + +- Bugfixes + +Minimum: NC 16 Server, Android 7.0 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/93 + + + +## 3.29.0 (April 24, 2024) + +- NC Assistant +- Client certificates +- Personal files view +- REUSE compliance +- Bugfixes + +Minimum: NC 16 Server, Android 7.0 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/89 + ## 3.28.2 (April 4th, 2024) - Bugfixes - Minimum: NC 16 Server, Android 7.0 Nougat For a full list, please see https://github.com/nextcloud/android/milestone/90 - - ## 3.28.1 (March 25th, 2024) - Bugfixes @@ -25,7 +73,6 @@ For a full list, please see https://github.com/nextcloud/android/milestone/90 - E2E sharing - Bugfixes - Minimum: NC 16 Server, Android 7.0 Nougat For a full list, please see https://github.com/nextcloud/android/milestone/88 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index aedac86..ae8a32d 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,6 +1,6 @@ In the Nextcloud community, participants from all over the world come together to create Free Software for a free internet. This is made possible by the support, hard work and enthusiasm of thousands of people, including those who create and use Nextcloud software. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2f0ad39..5fab39e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # [Nextcloud](https://nextcloud.com) Android app @@ -191,17 +191,17 @@ Source code of app: * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ ``` XML (layout) file: ```xml ``` diff --git a/Gemfile.lock b/Gemfile.lock index 234c43e..764030f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,42 +5,44 @@ GEM base64 nkf rexml - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) artifactory (3.0.17) atomos (0.1.3) - aws-eventstream (1.3.0) - aws-partitions (1.913.0) - aws-sdk-core (3.191.6) + aws-eventstream (1.4.0) + aws-partitions (1.1121.0) + aws-sdk-core (3.226.1) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.78.0) - aws-sdk-core (~> 3, >= 3.191.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.146.1) - aws-sdk-core (~> 3, >= 3.191.0) + logger + aws-sdk-kms (1.106.0) + aws-sdk-core (~> 3, >= 3.225.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.191.0) + aws-sdk-core (~> 3, >= 3.225.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - base64 (0.2.0) - cgi (0.4.1) + base64 (0.3.0) + cgi (0.4.2) claide (1.1.0) colored (1.2) colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) declarative (0.0.20) - digest-crc (0.6.5) + digest-crc (0.7.0) rake (>= 12.0.0, < 14.0.0) domain_name (0.6.20240107) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.110.0) - faraday (1.10.3) + excon (0.112.0) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -56,20 +58,20 @@ GEM faraday (>= 0.8.0) http-cookie (~> 1.0.0) faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) + faraday-em_synchrony (1.0.1) faraday-excon (1.1.0) faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-multipart (1.1.1) + multipart-post (~> 2.0) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) - fastimage (2.3.1) - fastlane (2.220.0) + fastimage (2.4.0) + fastlane (2.228.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -85,6 +87,7 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) @@ -108,10 +111,12 @@ GEM tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) + xcpretty (~> 0.4.1) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - fastlane-plugin-huawei_appgallery_connect (1.0.28) + fastlane-plugin-huawei_appgallery_connect (1.0.31) cgi + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) @@ -129,12 +134,12 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.0) + google-cloud-core (1.8.0) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.4.0) + google-cloud-errors (1.5.0) google-cloud-storage (1.47.0) addressable (~> 2.8) digest-crc (~> 0.4) @@ -150,36 +155,39 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.5) + http-cookie (1.0.8) domain_name (~> 0.5) - httpclient (2.8.3) + httpclient (2.9.0) + mutex_m jmespath (1.6.2) - json (2.7.2) - jwt (2.8.1) + json (2.12.2) + jwt (2.10.1) base64 - mini_magick (4.12.0) + logger (1.7.0) + mini_magick (4.13.2) mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.4.0) - nanaimo (0.3.0) - naturally (2.2.1) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.3.0) nkf (0.2.0) - optparse (0.4.0) + optparse (0.6.0) os (1.1.4) - plist (3.7.1) - public_suffix (5.0.5) - rake (13.2.1) + plist (3.7.2) + public_suffix (6.0.2) + rake (13.3.0) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.6) - rouge (2.0.7) + rexml (3.4.1) + rouge (3.28.0) ruby2_keywords (0.0.5) - rubyzip (2.3.2) + rubyzip (2.4.1) security (0.1.5) - signet (0.19.0) + signet (0.20.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -187,6 +195,7 @@ GEM simctl (1.6.10) CFPropertyList naturally + sysrandom (1.0.5) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -196,17 +205,17 @@ GEM tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) word_wrap (1.0.0) - xcodeproj (1.24.0) + xcodeproj (1.27.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (~> 3.2.4) - xcpretty (0.3.0) - rouge (~> 2.0.7) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) diff --git a/LICENSES/LicenseRef-XTrademarks.txt b/LICENSES/LicenseRef-XTrademarks.txt new file mode 100644 index 0000000..46b6983 --- /dev/null +++ b/LICENSES/LicenseRef-XTrademarks.txt @@ -0,0 +1,49 @@ +Trademark policy +April 2023 + + +You may not violate others’ intellectual property rights, including copyright and trademark. + +A trademark is a word, logo, phrase, or device that distinguishes a trademark holder’s good or service in the marketplace. Trademark law may prevent others from using a trademark in an unauthorized or confusing manner. + + +What is in violation of this policy? + +Using another’s trademark in a way that may mislead or confuse people about your affiliation may be a violation of our trademark policy. + + +What is not a violation of this policy? + +Referencing another’s trademark is not automatically a violation of X's trademark policy. Examples of non-violations include: + +* using a trademark in a way that is outside the scope of the trademark registration e.g., in a different territory, or a different class of goods or services than that identified in the registration; and +* using a trademark in a nominative or other fair use manner. For more information, see our Misleading and deceptive identities policy (https://help.twitter.com/en/rules-and-policies/twitter-impersonation-and-deceptive-identities-policy.html). + + +Who can report violations of this policy? + +X only investigates requests that are submitted by the trademark holder or their authorized representative e.g., a legal representative or other representative for a brand. + + +How can I report violations of this policy? + +You can submit a trademark report through our trademark report form (https://help.twitter.com/forms/trademark). Please provide all the information requested in the form. If you submit an incomplete report, we’ll need to follow up about the missing information. Please note that this will result in a delay in processing your report. + +Note: We may provide the account holder with your name and other information included in the copy of the report. + + +What happens if you violate this policy? + +If we determine that you violated our trademark policy, we may suspend your account. Depending on the type of violation, we may give you an opportunity to comply with our policies. In other instances, an account may be permanently suspended upon first review. If you believe that your account was suspended in error, you can submit an appeal (https://help.twitter.com/forms/general?subtopic=suspended). + + +Additional resources + +Learn more about our range of enforcement options (https://help.twitter.com/rules-and-policies/enforcement-options) and our approach to policy development and enforcement (https://help.twitter.com/rules-and-policies/enforcement-philosophy). + + +Legal disclaimer + +By using the X trademarks and resources on this site, you agree to follow the X Trademark Guidelines in our Brand Guidelines — as well as our Terms of Service and all other X rules and policies. If you have any questions, contact us at trademarks@x.com. + +A copy can be found at https://about.x.com/en/who-we-are/brand-toolkit and https://help.twitter.com/en/rules-and-policies/x-trademark-policy diff --git a/README.md b/README.md index 94c71a3..2c9f897 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # [Nextcloud](https://nextcloud.com) Android app :iphone: -[![REUSE status](https://api.reuse.software/badge/github.com/nextcloud/android)](https://api.reuse.software/info/github.com/nextcloud/android) [![Build Status](https://drone.nextcloud.com/api/badges/nextcloud/android/status.svg)](https://drone.nextcloud.com/nextcloud/android) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/80401cb343854343b4d94acbfb72d3ec)](https://www.codacy.com/app/Nextcloud/android?utm_source=github.com\&utm_medium=referral\&utm_content=nextcloud/android\&utm_campaign=Badge_Grade) [![Releases](https://img.shields.io/github/release/nextcloud/android.svg)](https://github.com/nextcloud/android/releases/latest) +[![REUSE status](https://api.reuse.software/badge/github.com/nextcloud/android)](https://api.reuse.software/info/github.com/nextcloud/android) [![Build Status](https://drone.nextcloud.com/api/badges/nextcloud/android/status.svg)](https://drone.nextcloud.com/nextcloud/android) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/fb4cf26336774ee3a5c9adfe829c41aa)](https://app.codacy.com/gh/nextcloud/android/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Releases](https://img.shields.io/github/release/nextcloud/android.svg)](https://github.com/nextcloud/android/releases/latest) [Download from Google Play](https://play.google.com/store/apps/details?id=com.nextcloud.client alt="Get it on F-Droid" height="80">](https://f-droid.org/packages/com.nextcloud.client/) +Signing certificate fingerprint to [verify](https://developer.android.com/studio/command-line/apksigner#usage-verify) the APK: +- APK with "gplay" name, found [here](https://github.com/nextcloud/android/releases) or distributed via Google Play Store +- APK with "nextcloud", found [here](https://github.com/nextcloud/android/releases) +- not suitable for Fdroid downloads, as Fdroid is signing it on their own +``` +SHA-256: fb009522f65e25802261b67b10a45fd70e610031976f40b28a649e152ded0373 +SHA-1: 74aa1702e714941be481e1f7ce4a8f779c19dcea +``` + **The Android client for [Nextcloud](https://nextcloud.com). Easily work with your data on your Nextcloud.** ![App screenshots](/doc/Nextcloud_Android_Screenshots.png "App screenshots") diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 0000000..59e6e3b --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later +version = 1 +SPDX-PackageName = "Nextcloud Android" +SPDX-PackageSupplier = "Nextcloud Android team " +SPDX-PackageDownloadLocation = "https://github.com/nextcloud/android" + +[[annotations]] +path = "gradle/wrapper/gradle-wrapper.jar" +precedence = "aggregate" +SPDX-FileCopyrightText = "2015-2021 the original authors" +SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = ["user_manual/images/android-1.png", "user_manual/images/android-2.png", "user_manual/images/android-3.png", "user_manual/images/android-4.png", "user_manual/images/android-10.png", "user_manual/images/davdroid-1-button-in-nextcloud-app.png", "user_manual/images/davdroid-2-install-davdroid.png", "user_manual/images/davdroid-3-enter-password.png", "user_manual/images/davdroid-4-specify-owner-email.png"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2016-2024 Nextcloud GmbH and Nextcloud contributors" +SPDX-License-Identifier = "AGPL-3.0-or-later" + +[[annotations]] +path = ["user_manual/conf.py", "user_manual/android_app.rst", "user_manual/index.rst", "user_manual/conf.py", "user_manual/Makefile"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2015-2016 ownCloud Inc., 2016-2024 Nextcloud GmbH" +SPDX-License-Identifier = "GPL-2.0-only" + +[[annotations]] +path = ["user_manual/images/android-11.png", "user_manual/images/android-12.png", "user_manual/images/android-13.png", "user_manual/images/android-14.png", "user_manual/images/android-15.png", "user_manual/images/android-5.png", "user_manual/images/android-6.png", "user_manual/images/android-8.png", "user_manual/images/android-9.png"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2015-2016 ownCloud Inc." +SPDX-License-Identifier = "GPL-2.0-only" + +[[annotations]] +path = ["app/src/**/res/mipmap-**dpi/ic_launcher.png", "app/src/**/ic_launcher-web.png", "src/generic/fastlane/metadata/android/en-US/images/icon.png", "src/versionDev/fastlane/metadata/android/en-US/images/icon.png", "app/src/main/ic_launcher-web-round.png"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2017-2024 Nextcloud GmbH " +SPDX-License-Identifier = "LicenseRef-NextcloudTrademarks" + +[[annotations]] +path = [".idea/**", "app/schemas/com.nextcloud.client.database.NextcloudDatabase/**.json", "app/screenshots/generic/debug/**.png", "app/src/main/res/values-**/strings.xml", "src/**/fastlane/metadata/android/**/**.txt", "src/versionDev/fastlane/metadata/android/**/changelogs/**.txt", "app/src/androidTest/assets/**", "app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker", "app/src/**/google-services.json", "app/src/main/res/drawable-**dpi/checker_16_16.png", "app/src/main/res/raw/encryption_key_words.txt", "app/src/main/resources/ical4j.properties", "app/src/main/res/drawable-**dpi/apk.png", "app/src/main/res/drawable-**dpi/fdroid.png", "app/src/main/res/drawable-**dpi/playstore.png", "app/src/main/res/drawable-**dpi/background.png", "app/src/main/res/drawable-**dpi/background_nc18.png"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2016-2024 Nextcloud GmbH and Nextcloud contributors" +SPDX-License-Identifier = "AGPL-3.0-or-later" diff --git a/SECURITY.md b/SECURITY.md index c6aea0b..b0adf57 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,28 +1,71 @@ # Security Policy -## Supported Versions +# 💡 TLDR: Report issues at [hackerone.com/nextcloud](https://hackerone.com/nextcloud) + +# Security Policy + +[Security](https://nextcloud.com/security/) is very important to us. + +If you believe you have found a security vulnerability that meets our definition of a security +vulnerability, please report is as described below. + +## Context + +Please review our [threat model and accepted risks](https://nextcloud.com/security/threat-model) to learn what +is currently considered a security vulnerability versus expected behavior. And review what is considered +[in scope or bounty eligible](https://hackerone.com/nextcloud/policy_scopes). -Only the latest version is supported. We release every second month a feature release (currently 3.x) and inbetween a bug fix release (3.x.y). ## Reporting a Vulnerability -Security is very important to us. If you have discovered a security issue with Nextcloud, -please read our responsible disclosure guidelines and contact us at [hackerone.com/nextcloud](https://hackerone.com/nextcloud). +**⚠️ Please do _not_ report security vulnerabilities through public GitHub issues.** + +If you have discovered a security matter with Nextcloud, please read our +[responsible disclosure guidelines](https://nextcloud.com/security/) and contact us at +[hackerone.com/nextcloud](https://hackerone.com/nextcloud). + Your report should include: - Product version - A vulnerability description - Reproduction steps +- Any other details you think are likely to be important -A member of the security team will confirm the vulnerability, determine its impact, and develop a fix. -The fix will be applied to the master branch, tested, and packaged in the next bug fix release. +### What to Expect + +You should receive an initial acknowledgement within 24 hours in most cases. + +A member of the security team will confirm the vulnerability, determine its impact, follow-up with any questions, +and coordinate the fix and publication. + +The fix will be applied to all applicable and still supported stable branches, tested, and packaged in the next security release. The vulnerability will be publicly announced after the release. Finally, your name will be added -to the [hall of fame](https://hackerone.com/nextcloud/thanks) as a thank you from the entire Nextcloud community. Note our -[threat model](https://nextcloud.com/security/threat-model) to know what is expected behavior. +to the [hall of fame](https://hackerone.com/nextcloud/thanks) as a thank you from the entire Nextcloud +community. +If the vulnerability involves an app that is not maintained by Nextcloud (i.e. hosted by the +Nextcloud project but community maintained, or hosted elsewhere), the security team will try to coordinate with the +current maintainer and help to get the issue fixed in similar fashion. -Please visit https://nextcloud.com/security/ for further information about security. +### Bug Bounties + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Details +on past bounty ranges can be found at [hackerone.com/nextcloud](https://hackerone.com/nextcloud). + +## Existing Security Advisories + +Published security advisories for the Nextcloud Server, Clients and Apps can be viewed at +[https://github.com/nextcloud/security-advisories/security/advisories](https://github.com/nextcloud/security-advisories/security/advisories). + +## Supported Versions + +Only the latest version is supported. We release every second month a feature release (currently 3.x) and inbetween a bug fix release (3.x.y). + +## Additional Information + +Please visit [https://nextcloud.com/security/](https://nextcloud.com/security/) for further information about Nextcloud security. +Please visit [https://nextcloud.com/security/threat-model](https://nextcloud.com/security/threat-model) for our threat model and accepted risks. diff --git a/SETUP.md b/SETUP.md index 36f8362..d1a6e6a 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,6 +1,6 @@ These instructions will help you to set up your development environment, get the source code of the Nextcloud for Android app and build it by yourself. If you want to help developing the app take a look to the [contribution guidelines][0]. diff --git a/app/.gitignore b/app/.gitignore index 828b797..e666d0b 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,3 +1,3 @@ # SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: AGPL-3.0-or-later +# SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only /build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index d2b1fd6..60582e0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,11 +2,11 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Tobias Kaminsky * SPDX-FileCopyrightText: 2024 Andy Scherzinger * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ import com.github.spotbugs.snom.Confidence import com.github.spotbugs.snom.Effort @@ -16,11 +16,11 @@ import org.gradle.internal.jvm.Jvm buildscript { dependencies { classpath "com.android.tools.build:gradle:$androidPluginVersion" - classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.12' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.6" - classpath "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2 - classpath 'com.karumi:shot:6.1.0' + classpath libs.spotbugs.gradle.plugin + classpath libs.kotlin.gradle.plugin + classpath libs.detekt.gradle.plugin + classpath libs.commons.httpclient.commons.httpclient // remove after entire switch to lib v2 + classpath libs.shot classpath "org.jacoco:org.jacoco.core:$jacoco_version" classpath "org.jacoco:org.jacoco.report:$jacoco_version" classpath "org.jacoco:org.jacoco.agent:$jacoco_version" @@ -28,40 +28,35 @@ buildscript { } plugins { - id "com.diffplug.spotless" version "6.20.0" - id 'com.google.devtools.ksp' version '1.9.23-1.0.20' apply false + alias(libs.plugins.kotlin.compose) + alias(libs.plugins.spotless) + alias(libs.plugins.kapt) + alias(libs.plugins.ksp) apply false } -apply plugin: 'com.android.application' +apply plugin: "com.android.application" -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-parcelize' -apply plugin: 'checkstyle' -apply plugin: 'pmd' +apply plugin: "kotlin-android" +apply plugin: "kotlin-parcelize" +apply plugin: "checkstyle" +apply plugin: "pmd" apply from: "$rootProject.projectDir/jacoco.gradle" -apply plugin: 'com.github.spotbugs' -apply plugin: 'io.gitlab.arturbosch.detekt' +apply plugin: "com.github.spotbugs" +apply plugin: "io.gitlab.arturbosch.detekt" // needed to make renovate run without shot, as shot requires Android SDK // https://github.com/pedrovgs/Shot/issues/300 if (shotTest) { - apply plugin: 'shot' + apply plugin: "shot" } -apply plugin: 'com.google.devtools.ksp' +apply plugin: "com.google.devtools.ksp" println "Gradle uses Java ${Jvm.current()}" configurations { configureEach { - exclude group: 'org.jetbrains', module: 'annotations-java5' // via prism4j, already using annotations explicitly - - // check for updates every build - resolutionStrategy { - cacheChangingModulesFor 0, 'seconds' - exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug" - } + exclude group: "org.jetbrains", module: "annotations-java5" // via prism4j, already using annotations explicitly } } @@ -72,39 +67,59 @@ configurations.configureEach { useVersion(checkerVersion) because("https://github.com/google/ExoPlayer/issues/10007") } + + if (requested.group == "commons-logging" && requested.name == "commons-logging") { + useTarget("org.slf4j:jcl-over-slf4j:1.7.4") + } } } // semantic versioning for version code def versionMajor = 3 -def versionMinor = 29 -def versionPatch = 3 +def versionMinor = 34 +def versionPatch = 1 def versionBuild = 90 // 0-50=Alpha / 51-98=RC / 90-99=stable + def ndkEnv = new HashMap() file("$project.rootDir/ndk.env").readLines().each() { - def (key, value) = it.tokenize('=') + def (key, value) = it.tokenize("=") ndkEnv.put(key, value) } -def perfAnalysis = project.hasProperty('perfAnalysis') +def perfAnalysis = project.hasProperty("perfAnalysis") + +def getConfigProperties() { + def props = new Properties() + def file = rootProject.file(".gradle/config.properties") + if (file.exists()) { + props.load(new FileInputStream(file)) + } + return props +} + +def configProps = getConfigProperties() android { // install this NDK version and Cmake to produce smaller APKs. Build will still work if not installed - ndkVersion "${ndkEnv.get("NDK_VERSION")}" + ndkVersion = "${ndkEnv.get("NDK_VERSION")}" - namespace 'com.owncloud.android' - testNamespace "${namespace}.test" + namespace = "com.owncloud.android" + testNamespace = "${namespace}.test" + androidResources { + generateLocaleConfig = true + } defaultConfig { - minSdkVersion 24 - targetSdkVersion 34 - compileSdk 34 + applicationId = "com.nextcloud.client" + minSdk = 27 + targetSdk = 35 + compileSdk = 35 - buildConfigField 'boolean', 'CI', ciBuild.toString() - buildConfigField 'boolean', 'RUNTIME_PERF_ANALYSIS', perfAnalysis.toString() + buildConfigField "boolean", "CI", ciBuild.toString() + buildConfigField "boolean", "RUNTIME_PERF_ANALYSIS", perfAnalysis.toString() javaCompileOptions { annotationProcessorOptions { @@ -121,7 +136,7 @@ android { testInstrumentationRunnerArgument "TEST_SERVER_URL", "${NC_TEST_SERVER_BASEURL}" testInstrumentationRunnerArgument "TEST_SERVER_USERNAME", "${NC_TEST_SERVER_USERNAME}" testInstrumentationRunnerArgument "TEST_SERVER_PASSWORD", "${NC_TEST_SERVER_PASSWORD}" - testInstrumentationRunnerArguments disableAnalytics: 'true' + testInstrumentationRunnerArguments disableAnalytics: "true" versionCode versionMajor * 10000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild @@ -139,8 +154,15 @@ android { flavorDimensions += "default" buildTypes { + release { + buildConfigField "String", "NC_TEST_SERVER_DATA_STRING", "\"\"" + } + debug { - testCoverageEnabled(project.hasProperty('coverage')) + testCoverageEnabled = project.hasProperty("coverage") + resConfigs "xxxhdpi" + + buildConfigField "String", "NC_TEST_SERVER_DATA_STRING", "\"nc://login/user:${configProps['NC_TEST_SERVER_USERNAME']}&password:${configProps['NC_TEST_SERVER_PASSWORD']}&server:${configProps['NC_TEST_SERVER_BASEURL']}\"" } } @@ -151,17 +173,17 @@ android { productFlavors { // used for f-droid generic { - applicationId 'com.nextcloud.client' + applicationId "com.nextcloud.client" dimension "default" } gplay { - applicationId 'com.nextcloud.client' + applicationId "com.nextcloud.client" dimension "default" } huawei { - applicationId 'com.nextcloud.client' + applicationId "com.nextcloud.client" dimension "default" } @@ -180,10 +202,9 @@ android { } } - testOptions { unitTests.returnDefaultValues = true - animationsDisabled true + animationsDisabled = true } } @@ -191,17 +212,18 @@ android { // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure packagingOptions { resources { - excludes += 'META-INF/LICENSE*' - pickFirst 'MANIFEST.MF' // workaround for duplicated manifest on some dependencies + excludes += "META-INF/LICENSE*" + excludes += "META-INF/versions/9/OSGI-INF/MANIFEST*" + pickFirst "MANIFEST.MF" // workaround for duplicated manifest on some dependencies } } tasks.register("checkstyle", Checkstyle) { configFile = file("${rootProject.projectDir}/checkstyle.xml") configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath - source 'src' - include '**/*.java' - exclude '**/gen/**' + source "src" + include "**/*.java" + exclude "**/gen/**" classpath = files() } @@ -210,26 +232,26 @@ android { ignoreFailures = true // should continue checking ruleSets = [] - source 'src' - include '**/*.java' - exclude '**/gen/**' + source "src" + include "**/*.java" + exclude "**/gen/**" reports { xml { - destination = file("$project.buildDir/reports/pmd/pmd.xml") + destination = layout.buildDirectory.file("reports/pmd/pmd.xml").get().asFile } html { - destination = file("$project.buildDir/reports/pmd/pmd.html") + destination = layout.buildDirectory.file("reports/pmd/pmd.html").get().asFile } } } - check.dependsOn 'checkstyle', 'spotbugsGplayDebug', 'pmd', 'lint', 'spotlessKotlinCheck', 'detekt' + check.dependsOn "checkstyle", "spotbugsGplayDebug", "pmd", "lint", "spotlessKotlinCheck", "detekt" buildFeatures { - dataBinding true - viewBinding true - aidl true + dataBinding = true + viewBinding = true + aidl = true compose = true } @@ -243,11 +265,11 @@ android { } lint { - abortOnError false - checkGeneratedSources true - disable 'MissingTranslation', 'GradleDependency', 'VectorPath', 'IconMissingDensityFolder', 'IconDensities', 'GoogleAppIndexingWarning', 'MissingDefaultResource', 'InvalidPeriodicWorkRequestInterval', 'StringFormatInvalid', 'MissingQuantity' - htmlOutput file("$project.buildDir/reports/lint/lint.html") - htmlReport true + abortOnError = false + checkGeneratedSources = true + disable "MissingTranslation", "GradleDependency", "VectorPath", "IconMissingDensityFolder", "IconDensities", "GoogleAppIndexingWarning", "MissingDefaultResource", "InvalidPeriodicWorkRequestInterval", "StringFormatInvalid", "MissingQuantity" + htmlOutput = layout.buildDirectory.file("reports/lint/lint.html").get().asFile + htmlReport = true } sourceSets { @@ -255,189 +277,251 @@ android { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } - composeOptions { - kotlinCompilerExtensionVersion = "1.5.11" + kapt { + useBuildCache = true } } dependencies { + // region Nextcloud library implementation("com.github.nextcloud:android-library:$androidLibraryVersion") { - exclude group: 'org.ogce', module: 'xpp3' // unused in Android and brings wrong Junit version + exclude group: "org.ogce", module: "xpp3" // unused in Android and brings wrong Junit version } + // endregion - // Jetpack Compose - implementation(platform("androidx.compose:compose-bom:2024.04.00")) - implementation("androidx.compose.ui:ui") - implementation("androidx.compose.ui:ui-graphics") - implementation("androidx.compose.material3:material3") - implementation("androidx.compose.ui:ui-tooling-preview:1.6.5") - debugImplementation 'androidx.compose.ui:ui-tooling:1.6.5' + // region Splash Screen + implementation libs.splashscreen + // endregion - compileOnly 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2' - // remove after entire switch to lib v2 - implementation "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2 - implementation 'org.apache.jackrabbit:jackrabbit-webdav:2.13.5' // remove after entire switch to lib v2 - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'com.google.android.material:material:1.11.0' - implementation 'com.jakewharton:disklrucache:2.0.2' - implementation "androidx.appcompat:appcompat:$appCompatVersion" - implementation 'androidx.webkit:webkit:1.10.0' - implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.exifinterface:exifinterface:1.3.7' - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.1" - implementation "androidx.lifecycle:lifecycle-service:2.8.1" - implementation "androidx.work:work-runtime:$workRuntime" - implementation "androidx.work:work-runtime-ktx:$workRuntime" - implementation "androidx.fragment:fragment-ktx:1.6.2" - implementation 'com.github.albfernandez:juniversalchardet:2.0.3' // need this version for Android <7 - compileOnly 'com.google.code.findbugs:annotations:3.0.1u2' - implementation 'commons-io:commons-io:2.16.1' - implementation 'org.greenrobot:eventbus:3.3.1' - implementation 'com.googlecode.ez-vcard:ez-vcard:0.12.1' - implementation 'org.lukhnos:nnio:0.3' - implementation 'org.bouncycastle:bcpkix-jdk18on:1.77' - implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.github.nextcloud-deps:sectioned-recyclerview:0.6.1' - implementation 'com.github.chrisbanes:PhotoView:2.3.0' - implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.28' - implementation 'com.github.nextcloud-deps:qrcodescanner:0.1.2.4' // 'com.github.blikoon:QRCodeScanner:0.1.2' - implementation 'com.google.android.flexbox:flexbox:3.0.0' - implementation('com.github.bumptech.glide:glide:3.8.0') { - exclude group: "com.android.support" - } - implementation 'com.caverock:androidsvg:1.4' - implementation 'androidx.annotation:annotation:1.7.1' - implementation 'com.vanniktech:emoji-google:0.18.0' + // region Jetpack Compose + implementation(platform(libs.compose.bom)) + implementation(libs.compose.ui) + implementation(libs.compose.ui.graphics) + implementation(libs.compose.material3) + debugImplementation(libs.compose.ui.tooling) + implementation(libs.compose.ui.tooling.preview) + // endregion - implementation "com.github.nextcloud-deps.hwsecurity:hwsecurity-fido:$fidoVersion" - implementation "com.github.nextcloud-deps.hwsecurity:hwsecurity-fido2:$fidoVersion" + // region Media3 + implementation libs.media3.ui + implementation libs.media3.session + implementation libs.media3.exoplayer + implementation libs.media3.datasource + // endregion - // document scanner not available on FDroid (generic) due to OpenCV binaries - gplayImplementation project(':appscan') - huaweiImplementation project(':appscan') - qaImplementation project(':appscan') + // region Room + implementation libs.room.runtime + ksp "androidx.room:room-compiler:$roomVersion" + androidTestImplementation libs.room.testing + // endregion - spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.13.0' - spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.6.4' + // region Espresso + androidTestImplementation libs.espresso.core + androidTestImplementation libs.espresso.contrib + androidTestImplementation libs.espresso.web + androidTestImplementation libs.espresso.accessibility + androidTestImplementation libs.espresso.intents + androidTestImplementation libs.espresso.idling.resource + // endregion - implementation "com.google.dagger:dagger:$daggerVersion" - implementation "com.google.dagger:dagger-android:$daggerVersion" - implementation "com.google.dagger:dagger-android-support:$daggerVersion" + // region Glide + implementation libs.glide + ksp libs.ksp + // endregion + + // region UI + implementation libs.appcompat + implementation libs.webkit + implementation libs.cardview + implementation libs.exifinterface + implementation libs.fragment.ktx + // endregion + + // region Worker + implementation libs.work.runtime + implementation libs.work.runtime.ktx + // endregion + + // region Lifecycle + implementation libs.lifecycle.viewmodel.ktx + implementation libs.lifecycle.service + implementation(libs.lifecycle.runtime.ktx) + // endregion + + // region JUnit + androidTestImplementation libs.junit + androidTestImplementation libs.rules + androidTestImplementation libs.runner + androidTestUtil libs.orchestrator + androidTestImplementation libs.core.ktx + androidTestImplementation libs.core.testing + // endregion + + // region other libraries + compileOnly libs.org.jbundle.util.osgi.wrapped.org.apache.http.client + implementation libs.commons.httpclient.commons.httpclient // remove after entire switch to lib v2 + implementation libs.jackrabbit.webdav // remove after entire switch to lib v2 + implementation libs.constraintlayout + implementation libs.legacy.support.v4 + implementation libs.material + implementation libs.disklrucache + implementation libs.juniversalchardet // need this version for Android <7 + compileOnly libs.annotations + implementation libs.commons.io + implementation libs.eventbus + implementation libs.ez.vcard + implementation libs.nnio + implementation libs.bcpkix.jdk18on + implementation libs.gson + implementation libs.sectioned.recyclerview + implementation libs.photoview + implementation libs.android.gif.drawable + implementation libs.qrcodescanner // "com.github.blikoon:QRCodeScanner:0.1.2" + implementation libs.flexbox + implementation libs.androidsvg + implementation libs.annotation + implementation libs.emoji.google + // endregion + + // region AppScan, document scanner not available on FDroid (generic) due to OpenCV binaries + gplayImplementation project(":appscan") + huaweiImplementation project(":appscan") + qaImplementation project(":appscan") + // endregion + + // region SpotBugs + spotbugsPlugins libs.findsecbugs.plugin + spotbugsPlugins libs.fb.contrib + // endregion + + // region Dagger + implementation libs.dagger + implementation libs.dagger.android + implementation libs.dagger.android.support kapt "com.google.dagger:dagger-compiler:$daggerVersion" kapt "com.google.dagger:dagger-android-processor:$daggerVersion" + // endregion - implementation 'org.conscrypt:conscrypt-android:2.5.2' + // region Crypto + implementation libs.conscrypt.android + // endregion - implementation "androidx.media3:media3-ui:$androidxMediaVersion" - implementation "androidx.media3:media3-exoplayer:$androidxMediaVersion" - implementation "androidx.media3:media3-datasource-okhttp:$androidxMediaVersion" + // region Library + implementation libs.library + // endregion - implementation 'me.zhanghai.android.fastscroll:library:1.3.0' + // region Shimmer + implementation libs.loaderviewlibrary + // endregion - // Shimmer animation - implementation 'io.github.elye:loaderviewlibrary:3.0.0' - - // dependencies for markdown rendering - implementation "io.noties.markwon:core:$markwonVersion" - implementation "io.noties.markwon:ext-strikethrough:$markwonVersion" - implementation "io.noties.markwon:ext-tables:$markwonVersion" - implementation "io.noties.markwon:ext-tasklist:$markwonVersion" - implementation "io.noties.markwon:html:$markwonVersion" - - implementation "io.noties.markwon:syntax-highlight:$markwonVersion" - implementation "io.noties:prism4j:$prismVersion" + // region Markdown rendering + implementation libs.core + implementation libs.ext.strikethrough + implementation libs.ext.tables + implementation libs.ext.tasklist + implementation libs.html + implementation libs.syntax.highlight + implementation libs.prism4j kapt "io.noties:prism4j-bundler:$prismVersion" + // endregion - // dependencies for image cropping and rotation - implementation 'com.vanniktech:android-image-cropper:4.5.0' + // region Image cropping / rotation + implementation libs.android.image.cropper + // endregion - implementation 'org.osmdroid:osmdroid-android:6.1.18' + // region Maps + implementation libs.osmdroid.android + // endregion - implementation('org.mnode.ical4j:ical4j:3.0.0') { - ['org.apache.commons', 'commons-logging'].each { + // region iCal4j + implementation(libs.ical4j) { + ["org.apache.commons", "commons-logging"].each { exclude group: "$it" } } + // endregion + // region LeakCanary if (perfAnalysis) { - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.13' + debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14" } + // endregion - // dependencies for local unit tests - testImplementation 'junit:junit:4.13.2' - testImplementation "org.mockito:mockito-core:$mockitoVersion" - testImplementation "androidx.test:core:$androidxTestVersion" - testImplementation 'org.json:json:20240303' - testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion" - testImplementation 'androidx.arch.core:core-testing:2.2.0' + // region Local Unit Test + testImplementation libs.junit.junit + testImplementation libs.mockito.core + testImplementation libs.test.core + testImplementation libs.json + testImplementation libs.mockito.kotlin + testImplementation libs.core.testing testImplementation "io.mockk:mockk:$mockkVersion" - testImplementation "io.mockk:mockk-android:$mockkVersion" + testImplementation libs.mockk.android + // endregion - // dependencies for instrumented tests - // JUnit4 Rules - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation "androidx.test:rules:$androidxTestVersion" - // Android JUnit Runner - androidTestImplementation "androidx.test:runner:1.5.2" - androidTestUtil "androidx.test:orchestrator:1.4.2" - androidTestImplementation "androidx.test:core-ktx:$androidxTestVersion" - - // Espresso - androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-web:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-accessibility:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion" - - // Mocking support - androidTestImplementation 'com.github.tmurakami:dexopener:2.0.5' // required to allow mocking on API 27 and older - androidTestImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion" - androidTestImplementation "org.mockito:mockito-core:$mockitoVersion" - androidTestImplementation("org.mockito:mockito-android:$mockitoVersion") - androidTestImplementation "io.mockk:mockk-android:$mockkVersion" - androidTestImplementation 'androidx.arch.core:core-testing:2.2.0' - androidTestImplementation "com.facebook.testing.screenshot:core:0.15.0" + // region Mocking support + androidTestImplementation libs.dexopener // required to allow mocking on API 27 and older + androidTestImplementation libs.mockito.kotlin + androidTestImplementation libs.mockito.core + androidTestImplementation(libs.mockito.android) + androidTestImplementation libs.mockk.android + androidTestImplementation libs.screenshot.core + // endregion + // region UIAutomator // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests - // androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' + // androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0" // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details - //androidTestImplementation "com.android.support:support-annotations:${supportLibraryVersion}" - androidTestImplementation 'tools.fastlane:screengrab:2.1.1' - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + // androidTestImplementation "com.android.support:support-annotations:${supportLibraryVersion}" + androidTestImplementation libs.screengrab + // endregion - implementation "com.github.stateless4j:stateless4j:2.6.0" + // region Kotlin + implementation libs.kotlin.stdlib + // endregion - // upon each update first test: new registration, receive push - gplayImplementation "com.google.firebase:firebase-messaging:23.4.1" - gplayImplementation 'com.google.android.play:review-ktx:2.0.1' + // region Stateless + implementation libs.stateless4j + // endregion - implementation 'com.github.nextcloud.android-common:ui:0.17.0' + // region Google Play dependencies, upon each update first test: new registration, receive push + gplayImplementation libs.firebase.messaging + gplayImplementation libs.play.services.base + gplayImplementation libs.review.ktx + // endregion - implementation "androidx.room:room-runtime:$roomVersion" - ksp "androidx.room:room-compiler:$roomVersion" - androidTestImplementation "androidx.room:room-testing:$roomVersion" + // region UI + implementation libs.ui + // endregion - implementation "io.coil-kt:coil:2.6.0" - - // splash screen dependency ref: https://developer.android.com/develop/ui/views/launch/splash-screen/migrate - implementation 'androidx.core:core-splashscreen:1.0.1' + // region Image loading + implementation libs.coil + // endregion } + configurations.configureEach { resolutionStrategy { - cacheChangingModulesFor 0, 'seconds' - force 'org.objenesis:objenesis:3.3' + force "org.objenesis:objenesis:3.4" eachDependency { details -> - if ('org.jacoco' == details.requested.group) { + if ("org.jacoco" == details.requested.group) { details.useVersion "$jacoco_version" } } } } +// Run the compiler as a separate process +tasks.withType(JavaCompile).configureEach { + options.fork = true + + // Enable Incremental Compilation + options.incremental = true +} + tasks.withType(Test).configureEach { + // Run tests in parallel + maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 + // increased logging for tests testLogging { events "passed", "skipped", "failed" @@ -465,7 +549,7 @@ if (shotTest) { showOnlyFailingTestsInReports = ciBuild // CI environment renders some shadows slightly different from local VMs // Add a 0.5% tolerance to account for that - tolerance = ciBuild ? 0.5 : 0 + tolerance = ciBuild ? 0.1 : 0 } } @@ -476,7 +560,7 @@ jacoco { spotbugs { ignoreFailures = true // should continue checking effort = Effort.MAX - reportLevel = Confidence.valueOf('MEDIUM') + reportLevel = Confidence.valueOf("MEDIUM") } tasks.withType(SpotBugsTask){task -> @@ -484,7 +568,7 @@ tasks.withType(SpotBugsTask){task -> String variantName = variantNameCap.substring(0, 1).toLowerCase() + variantNameCap.substring(1) dependsOn "compile${variantNameCap}Sources" - classes = fileTree("$project.buildDir/intermediates/javac/${variantName}/compile${variantNameCap}JavaWithJavac/classes/") + classes = fileTree(layout.buildDirectory.get().asFile.toString()+"/intermediates/javac/${variantName}/compile${variantNameCap}JavaWithJavac/classes/") excludeFilter = file("${project.rootDir}/scripts/analysis/spotbugs-filter.xml") reports { xml { @@ -492,12 +576,12 @@ tasks.withType(SpotBugsTask){task -> } html { required = true - outputLocation = file("$project.buildDir/reports/spotbugs/spotbugs.html") - stylesheet = 'fancy.xsl' + outputLocation = layout.buildDirectory.file("reports/spotbugs/spotbugs.html").get().asFile + stylesheet = "fancy.xsl" } } } ksp { - arg('room.schemaLocation', "$projectDir/schemas") + arg("room.schemaLocation", "$projectDir/schemas") } diff --git a/app/detekt.yml b/app/detekt.yml index 3e8ba26..91abf2b 100644 --- a/app/detekt.yml +++ b/app/detekt.yml @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: AGPL-3.0-or-later +# SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only build: maxIssues: 2 weights: diff --git a/app/lint.xml b/app/lint.xml index a157f37..fcbde65 100644 --- a/app/lint.xml +++ b/app/lint.xml @@ -1,12 +1,12 @@ @@ -22,13 +22,17 @@ - + regexp="screenshot_01_gridView|screenshot_02_listView|screenshot_03_drawer|screenshot_04_accounts|screenshot_05_autoUpload|screenshot_06_davdroid" /> + + + + + + - - + @@ -56,7 +60,7 @@ - + @@ -77,4 +81,8 @@ + + + + diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json index 10d076c..3dd2580 100644 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json @@ -1206,4 +1206,4 @@ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '082a63031678a67879428f688f02d3b5')" ] } -} \ No newline at end of file +} diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/82.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/82.json new file mode 100644 index 0000000..e16d7c1 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/82.json @@ -0,0 +1,1233 @@ +{ + "formatVersion": 1, + "database": { + "version": 82, + "identityHash": "e78b1402db9da7caff78c46fff585672", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e78b1402db9da7caff78c46fff585672')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/83.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/83.json new file mode 100644 index 0000000..c27bba8 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/83.json @@ -0,0 +1,1245 @@ +{ + "formatVersion": 1, + "database": { + "version": 83, + "identityHash": "365a8731a100a61ae5029beb74acd02e", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '365a8731a100a61ae5029beb74acd02e')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/84.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/84.json new file mode 100644 index 0000000..b703cbe --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/84.json @@ -0,0 +1,1301 @@ +{ + "formatVersion": 1, + "database": { + "version": 84, + "identityHash": "70f2e2adb603afda7f87dbfb3b902e02", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_parent_path` TEXT, `offline_operations_type` TEXT, `offline_operations_path` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentPath", + "columnName": "offline_operations_parent_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '70f2e2adb603afda7f87dbfb3b902e02')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/85.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/85.json new file mode 100644 index 0000000..5e2a33b --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/85.json @@ -0,0 +1,1301 @@ +{ + "formatVersion": 1, + "database": { + "version": 85, + "identityHash": "2d24b9210a36150f221156d2e8f59665", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2d24b9210a36150f221156d2e8f59665')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json new file mode 100644 index 0000000..2571f61 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json @@ -0,0 +1,1331 @@ +{ + "formatVersion": 1, + "database": { + "version": 86, + "identityHash": "277489b9d4a6ee84f96d09dea39591ba", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '277489b9d4a6ee84f96d09dea39591ba')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/87.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/87.json new file mode 100644 index 0000000..2bce6bc --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/87.json @@ -0,0 +1,1337 @@ +{ + "formatVersion": 1, + "database": { + "version": 87, + "identityHash": "c67369ca15672b4c84289aa188f49e50", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c67369ca15672b4c84289aa188f49e50')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/88.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/88.json new file mode 100644 index 0000000..9255924 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/88.json @@ -0,0 +1,1343 @@ +{ + "formatVersion": 1, + "database": { + "version": 88, + "identityHash": "72369823c54307097d8ca60cf6944e2a", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '72369823c54307097d8ca60cf6944e2a')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/89.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/89.json new file mode 100644 index 0000000..e54001f --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/89.json @@ -0,0 +1,1349 @@ +{ + "formatVersion": 1, + "database": { + "version": 89, + "identityHash": "7a70f9151914c24eb0e5350316b126f5", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7a70f9151914c24eb0e5350316b126f5')" + ] + } +} diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/90.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/90.json new file mode 100644 index 0000000..c0b53e5 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/90.json @@ -0,0 +1,1355 @@ +{ + "formatVersion": 1, + "database": { + "version": 90, + "identityHash": "93eb4d5fbf952984b6fc2df9f7c369e1", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER, `attributes` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '93eb4d5fbf952984b6fc2df9f7c369e1')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/91.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/91.json new file mode 100644 index 0000000..a338fc4 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/91.json @@ -0,0 +1,1195 @@ +{ + "formatVersion": 1, + "database": { + "version": 91, + "identityHash": "16f8a78a87d896adf01d545ed83142e5", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT" + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT" + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT" + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT" + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER" + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER" + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT" + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT" + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT" + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT" + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT" + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT" + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT" + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT" + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER" + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER" + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER" + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT" + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT" + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT" + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT" + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT" + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER" + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER" + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT" + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER" + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT" + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT" + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER" + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER" + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER" + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER" + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER" + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT" + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT" + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT" + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT" + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT" + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT" + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT" + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER" + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT" + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT" + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER, `attributes` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT" + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER" + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT" + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT" + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT" + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT" + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER" + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER" + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER" + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER" + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT" + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "recommended_files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `directory` TEXT NOT NULL, `extension` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `has_preview` INTEGER NOT NULL, `reason` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "directory", + "columnName": "directory", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extension", + "columnName": "extension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '16f8a78a87d896adf01d545ed83142e5')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/92.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/92.json new file mode 100644 index 0000000..092bf97 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/92.json @@ -0,0 +1,1200 @@ +{ + "formatVersion": 1, + "database": { + "version": 92, + "identityHash": "aeef27ff00555d37d8605e760a446863", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT" + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT" + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT" + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT" + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER" + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER" + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT" + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT" + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT" + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT" + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT" + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT" + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT" + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT" + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER" + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER" + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER" + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT" + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT" + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT" + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT" + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT" + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER" + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER" + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT" + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER" + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT" + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT" + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER" + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER" + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER" + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER" + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER" + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT" + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT" + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT" + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT" + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT" + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT" + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT" + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER" + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT" + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT" + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER, `attributes` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT" + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER" + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT" + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT" + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT" + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT" + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER" + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER" + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER" + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER" + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT" + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "recommended_files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `directory` TEXT NOT NULL, `extension` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `has_preview` INTEGER NOT NULL, `reason` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `account_name` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "directory", + "columnName": "directory", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extension", + "columnName": "extension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'aeef27ff00555d37d8605e760a446863')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/93.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/93.json new file mode 100644 index 0000000..c619e1b --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/93.json @@ -0,0 +1,1205 @@ +{ + "formatVersion": 1, + "database": { + "version": 93, + "identityHash": "bbaa274a7bcf9daf381451c8e77d6930", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT" + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT" + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER, `user_status_supports_busy` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT" + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT" + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER" + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER" + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT" + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT" + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT" + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT" + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT" + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT" + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT" + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT" + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER" + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER" + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER" + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT" + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsBusy", + "columnName": "user_status_supports_busy", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT" + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT" + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT" + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT" + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER" + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER" + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT" + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER" + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT" + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT" + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER" + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER" + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER" + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER" + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER" + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT" + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT" + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT" + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT" + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT" + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT" + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT" + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER" + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT" + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT" + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER, `attributes` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT" + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER" + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT" + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT" + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT" + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT" + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER" + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER" + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER" + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER" + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT" + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "recommended_files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `directory` TEXT NOT NULL, `extension` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `has_preview` INTEGER NOT NULL, `reason` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `account_name` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "directory", + "columnName": "directory", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extension", + "columnName": "extension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bbaa274a7bcf9daf381451c8e77d6930')" + ] + } +} diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/94.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/94.json new file mode 100644 index 0000000..203afe3 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/94.json @@ -0,0 +1,1210 @@ +{ + "formatVersion": 1, + "database": { + "version": 94, + "identityHash": "84d16467d3052f332b38942987052f00", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT" + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT" + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER, `user_status_supports_busy` INTEGER, `windows_compatible_filenames` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT" + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT" + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER" + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER" + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT" + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT" + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT" + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT" + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT" + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT" + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT" + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT" + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER" + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER" + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER" + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT" + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsBusy", + "columnName": "user_status_supports_busy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWCFEnabled", + "columnName": "windows_compatible_filenames", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT" + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT" + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT" + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT" + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER" + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER" + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT" + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER" + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT" + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT" + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER" + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER" + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER" + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER" + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER" + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT" + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT" + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT" + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT" + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT" + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT" + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT" + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER" + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT" + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT" + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER, `attributes` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT" + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER" + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT" + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT" + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT" + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT" + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER" + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER" + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER" + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER" + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT" + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "recommended_files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `directory` TEXT NOT NULL, `extension` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `has_preview` INTEGER NOT NULL, `reason` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `account_name` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "directory", + "columnName": "directory", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extension", + "columnName": "extension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '84d16467d3052f332b38942987052f00')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/95.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/95.json new file mode 100644 index 0000000..694e08c --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/95.json @@ -0,0 +1,1215 @@ +{ + "formatVersion": 1, + "database": { + "version": 95, + "identityHash": "a2a95af369b3e75d48b6c454d1fe6c2d", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT" + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT" + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER, `notes_folder_path` TEXT, `default_permissions` INTEGER, `user_status_supports_busy` INTEGER, `windows_compatible_filenames` INTEGER, `has_valid_subscription` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER" + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT" + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT" + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER" + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER" + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT" + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT" + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT" + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT" + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT" + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER" + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT" + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT" + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER" + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT" + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT" + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT" + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER" + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER" + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameCharacters", + "columnName": "forbidden_filename_characters", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNames", + "columnName": "forbidden_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFileNameExtensions", + "columnName": "forbidden_filename_extensions", + "affinity": "INTEGER" + }, + { + "fieldPath": "forbiddenFilenameBaseNames", + "columnName": "forbidden_filename_basenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", + "affinity": "INTEGER" + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER" + }, + { + "fieldPath": "notesFolderPath", + "columnName": "notes_folder_path", + "affinity": "TEXT" + }, + { + "fieldPath": "defaultPermissions", + "columnName": "default_permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "userStatusSupportsBusy", + "columnName": "user_status_supports_busy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWCFEnabled", + "columnName": "windows_compatible_filenames", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasValidSubscription", + "columnName": "has_valid_subscription", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT" + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT" + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER, `internal_two_way_sync_timestamp` INTEGER, `internal_two_way_sync_result` TEXT, `uploaded` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT" + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT" + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER" + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER" + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT" + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER" + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER" + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT" + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT" + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT" + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER" + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER" + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER" + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT" + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER" + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER" + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT" + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT" + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT" + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT" + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER" + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT" + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT" + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT" + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySync", + "columnName": "internal_two_way_sync_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "internalTwoWaySyncResult", + "columnName": "internal_two_way_sync_result", + "affinity": "TEXT" + }, + { + "fieldPath": "uploaded", + "columnName": "uploaded", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER" + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT" + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT" + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER, `attributes` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT" + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER" + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT" + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT" + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER" + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT" + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER" + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT" + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER" + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT" + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT" + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER" + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER" + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER" + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT" + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER" + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER" + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT" + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT" + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER" + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER" + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER" + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER" + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER" + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER" + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "offline_operations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `offline_operations_parent_oc_file_id` INTEGER, `offline_operations_path` TEXT, `offline_operations_type` TEXT, `offline_operations_file_name` TEXT, `offline_operations_created_at` INTEGER, `offline_operations_modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "parentOCFileId", + "columnName": "offline_operations_parent_oc_file_id", + "affinity": "INTEGER" + }, + { + "fieldPath": "path", + "columnName": "offline_operations_path", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "offline_operations_type", + "affinity": "TEXT" + }, + { + "fieldPath": "filename", + "columnName": "offline_operations_file_name", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "offline_operations_created_at", + "affinity": "INTEGER" + }, + { + "fieldPath": "modifiedAt", + "columnName": "offline_operations_modified_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + }, + { + "tableName": "recommended_files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `directory` TEXT NOT NULL, `extension` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `has_preview` INTEGER NOT NULL, `reason` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `account_name` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "directory", + "columnName": "directory", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extension", + "columnName": "extension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a2a95af369b3e75d48b6c454d1fe6c2d')" + ] + } +} \ No newline at end of file diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_empty.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_empty.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_empty.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_empty.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_empty_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_empty_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_empty_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_empty_light_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_error.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_error.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_error.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_error.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_error_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_error_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_error_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_error_light_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_loading.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_loading.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_loading.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_loading.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_black.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_blue.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_blue.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_white.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_dark_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_black.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer_light_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.ActivitiesActivityIT_showActivities_light_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login.png b/app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login.png rename to app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_black.png b/app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_blue.png b/app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_blue.png rename to app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_white.png b/app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_dark_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_black.png b/app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.AuthenticatorActivityIT_login_light_white.png diff --git a/app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open.png b/app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open.png new file mode 100644 index 0000000000000000000000000000000000000000..2b44c91846ac680d26113f1e9644cb6a2a98698f GIT binary patch literal 56958 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 Om%FRim6i{0=KKndW0G0` literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_dark_black.png b/app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_dark_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_dark_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_dark_blue.png b/app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_dark_blue.png rename to app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_dark_white.png b/app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_dark_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_dark_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_light_black.png b/app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_light_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_light_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.CommunityActivityIT_open_light_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityIT_shareToCircle.png b/app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityIT_shareToCircle.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityIT_shareToCircle.png rename to app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityIT_shareToCircle.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityIT_showAccounts.png b/app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityIT_showAccounts.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityIT_showAccounts.png rename to app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityIT_showAccounts.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityIT_showShares.png b/app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityIT_showShares.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityIT_showShares.png rename to app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityIT_showShares.png diff --git a/app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png b/app/screenshots/generic/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png new file mode 100644 index 0000000000000000000000000000000000000000..5c12e09cc3236ac7fce26d270588f3e5701f8cd8 GIT binary patch literal 21927 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_blue.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_blue.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_white.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_dark_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_openDrawer_light_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_black.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_blue.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_blue.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_white.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_dark_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_black.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_open_light_white.png diff --git a/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_showPowerCheckDialog.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_showPowerCheckDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..c99e6641857dfb2a86a8123d68fb61960a6faea9 GIT binary patch literal 9383 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..99e5eb969be4c642f92c90551b38d49d20c68ae9 GIT binary patch literal 30771 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
8kdE-Ew5!_?dEAA<2YfKkShUd77U7Q@ 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_black.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_blue.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_blue.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_white.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_black.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_black.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_white.png b/app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_white.png rename to app/screenshots/generic/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png b/app/screenshots/generic/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png new file mode 100644 index 0000000000000000000000000000000000000000..0d8d7edc40a1c131aa574d9439517c602506f5ba GIT binary patch literal 22174 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 KZOxAFFaAG|ehira literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.nextcloud.client.etm.EtmActivityTest_overview.png b/app/screenshots/generic/debug/com.nextcloud.client.etm.EtmActivityTest_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..774de814bc051e8f3d5db9967f1c035565f94aef GIT binary patch literal 14791 zcmeHtXIN8Nzi$8)92I0xQGs9?K}Ebx$iyax%WQL{cyf4AC_#^US+Re z``&bRks9{J>0r#lnq z|9hKQ0ldn-zqS9>S7+Qi{~wL|$9w#9RH@^~ByPf^iu&b85wpf9Oa0R?nR;w)bItd8 zB5aS|KyV$Cfq(>YDEJcMn)&Ps+0?mse%kab887$1QxmM>xv{pVt+)r@NY0;L;MgTd zT3rQ@^7Q>ji6!tsy*fsQc-xaf?a>@&#Ikr)jE{I1d8zm9YW)O5kr6QcQHh;`;_Fdj zuWu6IcE;v169JhMw0ck2MGId=CVmzOPkrJUvlFLkZtwO&;FW)!p+g5@-1#G(skvC&C{n&J>geV@i)eHObIiR=Y!*n0k7QfVFSdjD3 z(H!&hFyd45boa6JlhDy<4|l=@C1T)3<#g|viWlYLF@bY^1XJX=Dw{>uwxn z+Xb%QbHDsYeP3!0HDW@o6_~k)LN-aBr*1pPuIAdPmPgBn1aQ z>hA&X<*FqV6XFl?{2sqdMOjgobx9c0WINQ<$-S(ub2T#IR|C(8%a&hm91}F?gwCCs zZjSYxpmnp&^D$vjmb_b+6kJdJ{1C#GYiL$6ZTlK@nWFB&#Zua8X}_yiK|%O(Xvwq4 zfy)qDmPbedhM%E;Kt``5dG(IjpDD}NBI%~^7Hx%ea}>SIq`&$KUbu2^U8h@TVk&;8Bb!FAl1JMu%5Z8U=MFP> z;VRqke1Z%^3YBBpRIm{<3nw<7*xQB(#LMVHQsdlx?7n6!?c%D-W=l9+jy-s`P4(8G zf(u=~gu%5|9)IIV5BaaWQvJq2+Y#TaFgq4Wq! zHN7JC`5~I8VM4-$CiywlCfF+OAZya9v@3Gix`k#nU+=*PXUr&!iyzGNCxRm-&&u|b zc8tb^Um3=A;={o}Vbmubssz-L6-1YtL^~o&3 zRP)1Q2AJPT3)0v;I4RD$XMY-P>+i4Us_=w~MGu%CE-JoGibgov1^IpA#Fl6* z#^psa>7;l>W_Q(thfdN9`x&mdDKM!RPM9lUHlz>4DC7$Iq-PzP18lH^3Jjxkg7OTW z7e640RFOT%a0;v<&Q>&7*hY2btlLi+(`MIEx{y`!xyz8+P3%rimD(eoHoVtYW@PBS zl<+$`vV;anpPI-R20OXxCX`K;_uC;C7T^aty-nrJ;%L*6jj+v{uZ^_WJ-6XYgU+t- zZ2c4s7{yjWn5j(iv%asNBGaarG%WI)f;!lOnTY$4cH?FLX_A8DkjX+&x6vk-wGYic z)E<63>q5=DczVIV(4lNso6fBB@>`rXyM{tBZXjW;i(Us&VQZ$Y@+g&2vYfTd(}hsr zV|*UGX)nYY3boy0T-#MK%H&0~^6JoC?i++I-bf)hu93>dryVh7m_VX3Ufc) zn!qwV)L<+x`t7I@W7M=(*LXO=Ruqw&6%ITc5dbA<9K97~ihbhdl9~yDSC?~|lLsQ# z$8Mq|^F(N^xtZ1IvMI-X#V)ya5RB45?gxffBK+)m%V_o}Kb4JMz+Q5QM9VnpLKo`g zvP9<0tnvO@t#=?zIO{q#Yr0fjxOFL=wi!WRu=)8oBU*-&SeU9b15^7_Su7?dh7`X5_8P-dQ<^I{AA9{6_Hj*8MP=nix2 z)zCBJ=%r-J+!(Ju#jCR1-#HsX3WRhwj;*X&!Fpki`4&n@!?fr*=1P?PjyBe*%JL!G z(w-S9LywKiuX0f#_S395H`g|#5JkO&v5Pf>E@uZkfbWksM}A$u9v3C^uhE9BV~r=D zS*rsxtT_AvM#JS>6+dtfR+d#JDK0fs?+e-O(OfGL_I1r<*(FRoG2BnvN2?teX^5@sGWmJX!3#CU3hv8}3U=^{EK5e!Ybchw`P z(u>6>JROsML5l|=UG~Iqy(QeABhU2MVRLWmLdPcVIOiY#c+K2?gY8Ux`|+ps)q88T zNp2}Rw<0179aC#Aay4K0-@iW2OhpwkkiyX7sn3rjU+}*zWQ? zXC@QVXw^L)X@57{+x6_vDOuqo%f(F77M{aNZT+mLySm4%Kdu7~AMnXW#ukbr;+KCP zTi&?~^I3c6g%MFzqi7o`ZZvv<(KI?7GFah8sw=39hV|%yQIIm|Qe!_&C%c!pJhmKX zo#vYXts3Q2@Mo_@P{k*v2tCgTOnHJmK!X~IV;a{#HXhi^sonf}6XO~4$EmXbYeGXR z{EJ%$10!f{X(yl7Y@+x){(+D&4rVTxY?GQ~hd&4S8PV0n6XQH`zs%G0CEFI8Yd7(D zg@(pP&3w@-}5L8Ij zVuN;cD}fT#L23~oyF!&4cB-INmjCEej%Rh`TG_&^0v#F2WY7(pjqG>Wt@mq3f|dxI zu}Yx$9o?wmD|L4gn=dJpn3^E=xOWiV~6bM#dM5nP#vAuLC63^OuuT5Isa{#1{)>;P+cb)`y`xQyJqYDzY88tJYzan3 zZW^zMwLPn4a8^iCzX5Pm%@+GFHtuFSD_mBITE`71lT8I817d6K{;F{e^Ky#Q+xHoY zd`!b6zBgTl>Z)PX%KE67*C&P}GCISz;)5v5s97;(Y4OWDjXuAE_{ju45E0r@iHoBo0j9cWdY8;G0jeu+6g-PO3@0@pw z1N%ms?*yPmGvLwk+rlsJu+KuZ)16Xm;19AP@FsJ-7I3Y`oR$jvoCJJ!E!IRd*cOAJ zQnB&8R_)ArC>ri3a<-mgWqY3rpXxf3VM09Mb$FUD>^%da#e31b6A;X{k1y+LrVsfe z^*?92PCf@4}h%|k84B}C&)Tn+Ipr%F(qZo4a{9&t!^KlcO z9r}9SVLR#OL4%*C^-J>evy4690S$k?sUJ4+7e2}FF~%2j&Q|)nQ{UWB_Hwqd{fXy4@Y2tx0#brkl&_wOp-jEcLycYVpm|b||w zCt5n0i{y(b*$}5j&JR2C4oT;pi!?uDn1k!~(^E9o@ua>sW_?atH|)F@5}V~{ok>%{ zi}4eWf0d)M8NE&k%PA2bZk;ijwg1Vn>F0~AB@LU!9Z8ovk|8Ns$W?wtcF4(?ydl^5 z@VBN8*pm*rm>=bq?w>2u9Q0$#ujQ$+*p^6N{aB4*=sYy~WXK2Yv15rLs2lwUx{EyG zer});4;sZ^C5$*kg=a4nK!?A>S~#4`k8|d@V`g!6r2}OfI{X_PKm%pCx{=x|g-}6L z*IXIA5Nj@4JUG`)at=nwm!*P@kA}!P$fk$tx<4F*q;RBX3>{DF#|!WOaYPVr-6c`D zP()77pvwTWz$xt%GT5LsGvsWazd@YqDm2kV>|T?&ErYphkkg1ns}sUD8xNurnH?)j zo31RYarLIq@6K{&1AO&w=eTL>qeYBZ(nqdXhs9NL+n`MByc%faPTK(SC^PFL_N*6; zoiJm0yb-lr4eVTnPKDrkf|}k5ql$8Oi0^A{#K#3?;=`#(UvItD=(F#a=JB*MmqHq9 zuH`jynyjeqTz~zZs|j60QtfPQrV|AeS02&~e$~O+ob7 zXw1M;a2bl962V%7V_{YP_sEBL zb>JIC(&u8HbQU>rDztS_dPhfC=2@cg=GwRHCvh9ltHS5K;VB-(!_x5sJQOuh@zy=4 zh`D}|X>gMdOb98#;v;~=#c;iB4Q(UpbUFfSQ;;B!;oO7}mXzE&m0=3W$48`B1A7u| zB3lnP24-1-)P$Dpv|I;NOq{nBf*2R$9_IzO9Y~vc&^SCE3o2Aqfw}kgz~EM<71mnK zy4@@dP|f(9CWqKirU!DN+aaA|;$%aY!^1KakG6xlRh5ZE-a_+Bf2@Lgs{|Eiq_Xk>nl)v`<3j_sz@6U&%A%&33P2VwMslo`} z;=P8kS+_MH;eES;L*Y@wFBLbcS-x~v=L0DK%)-HJ!DJUwYoccJEC@t2?@m(R=CYPk zX5gLCI8-z?>m%t_Q5RHz?2sFTtJg8hZi)68<_%2p++3`|v&tai_uYuNCbhZP=jV`u zMPukWoOSbla<6xu;@TtA@tq1Y;FT60kg1UE(8|v7%5rFGkp}F`uRx3?-{3n9;0| zzm1SBK%cL`%cT-#md(;!RTkL0nw=3r>Aci?TRYW>=7l;2C(U(PaZW>;zxedOCWGUx zVbb@AOz!w193#m*h~hLIobILYL$hNy`RFLj7lgWOnpbQ*dgf(`5thhrABkX1l=aBMF;@s!v3W}wua7B z1^;zXl74Qw`SOc)@d{P}_Dg=FX)6ymbMl!BeRvJ%X|Ew3yYXvlAgtjwm;4?_5w_j z9WYVOc-WT&2`k%pwG^NvjsZEm z!NX}oCA&gAc-I>ZFxsEBWSGAwu2WKQZZ5}Hzuvvv?lo$^_XI7{0ZWb=<1G>n5NL>) zo=#>^p%VZ#{gz~1*wbry=)`7mVZ0zIImhVyT@5_~12{Qno-QoSm0L(o{BU8nlcljM zFW$O#f5j1O76(FGc7Al7waA-*DMHzk<9x{Q@Ww z-p^MXal>$lV%(2kA*B?28=VODlB=q3dz9^fe^abwSd6uizIRdrPPg00+7N&e3G_Z$ z66OOXA)&m-`O;sEO=FCYR>19DqavP=L^FYsC4MyOvIADzm^Vz;Hu22K)*fGAY-8Wf zxvM=sy~H7U&xn`OhR=4W*cH02j#=JQ<9n>>LhE6I)ga zur#~S`@kavhvs;pn0X#NJy>(ZbnnPmL0oI5GuA0L%8BXFvjJQ+hqx1W`K+hMkR5hs zVtAJgw61rZsfz8L&0|}YHnJl#xpa6lE6_8`h3j~|@&MRqRJqoW!v%XyXvQhz;)EF+ zuvcG+Tt@fkKcT&#za)!kt?X()vTc0Mv`kh@4?)iG>RS%@){yhqWBi(u0<=Nh`|)?j zKUXI_zK{O}tD)5s*8KU7PB^=EJKM=d@*~D8^aK4VR=XI|ytvCLIc2^jDapJ!M+Tm4 zxvm6KCZ^Tb1ZuYZvPQk@2;;Po6>vSbgwq?nf?V$oT)%bAl+rYCNYQlI`)zGBZ_GOJ z0D#ZqQ-Q1=T*J zd%A&>4k#m}b1KRKgu1%$+%y8T==g7B(ti#aR?y|Hlj>1H^v8ic-3=s;PT>p|$;i7p~UFCWK6f!G@o zZIGPhYCwiLA0hUsHx`=~wM}L%!up<1CF2p7tzBw3$y05IK{uQ?u{A%kuCIt5wD3OJ zDh^(*HJeqSLstf&&AW@7z0@cCWy3<%Ln4rEKpznNP zw#UGHO-E5HK*Y$bYxo5(Cewc008IuJHmbl(phnn-b$$aKLz$7|v!Vy&kB#HM)mnt- zpAkDH6kf#IilWiLoljuhLe{h%o*qjvD34OyA3M|<7&zgrK$~|Kj1Is0@H{st-vIu@ z2d8~@PO9kY!jw<*{>q+4iEs`YyN%}+JnhUkc#+fBYip>H+mu^DlD8eijMz_$ z=$seyn+Xcp?U+3O`;K{N59wSLb^99W+uQ59*^u|-3sF;gs1?Ckd2I-RwaEv^ zPgIr$la~2L0i2PTfZpP4)_C-EaW=iF4a$r*^TG^mZUEqRml%)RJe~gGhc7-Sxi^AOLEx?o!xwvxD>qPkn!-D545?2*vu{_|3 zn2!*CLG-Pj!k(cA(``|EAeZ^0Lt;(yS$UMKnwG!x%+xP0BZ?V7tCskB6l>TnzdBdw zu#r5za_g?3O?GQEW^Ok@?zBYSCK(-cvN$k4`{Ez^O$eyfsT(rEiQH!-&Z|q?BPZDh zuZxdN(`L(a>s(g=wd6wvz7(Cub6-K!(qOLX_49IZY9F;@isfLt7@$6XG^lFp)4l1Y za71h(c=j<=bhA`rm7X4Qm2^wO(0-D<&&kSco$$<{RK*`AjCDt*VjpY|p#>{Qh#+pa2K z9LI*gIh!jQ9T0S@Xq@^Bc3G6{sU4Pq!Xf8GFg@*4>V7VQ^!u^iPw`?WN|rHoluXhb zu1RcO3W{NHAr)hvq6;~$B%w!QN%0xqD zqG-}g4_4BsB3m&S6eskcW4Cl2HO<#Cy}Es6Y8h$*_O5gdWmiud3kcRF`qrt2b&YBd z+25Ebn<=vkRn#4=!fbqN_dgo;qw+CCj`m8eWM#8i6PCpXL&W?C^SZ z=~t3`^30iN^5BK+Sc~pl7!!?PoB3Klph-UcqQMuhh{go-yYS?rac{ck)zyS1YC>PX zr!*$GmUx~!Z6a2?t(L6Dd~3yR%5b#)JTbBd{CTU=7Gt5$nCIDNDX#*!{PJkW2v;&X zNk8i6;^K$46CJs(oBcc2NUNw_UDTARY9aviXvOe*A=?&PH}#|ID*`mt*nHXOkN_!?S|L(1?W-Ury|` zN1>~`)@fSW7p_nN3%B<1Hw((Jd^1_|Qr~u4ZzC@OHP8raZ&U?Gg*qg{tAhf?I$9hb ziHg{E@MQ;pggVnuB}h}r`oJ}_>=06ZjbW~K{&;YtbE$p z7p^8A9+lYqZ-iUL>1#tYC+$(vR8Nrop37x?l(VL9Rp9DC!1ueTf?gIP??g$t5Wk9uC%sQ-5C zv5K0|aX%^rD#-5Nej9xYC25|Ag(4ox>D(O1>Xc+knUAM6p%1b>Arx-d_A{c%+JI<3 z`@L^f!S&@+7B||npV((}0WMjo-||Hj z;DOHoeJtKy?@gg%Kc#<LY#Ve14r~W7 zivp-<*_wwNbnUR(>%Dm+5K(w0jqx@>+1aB|P-63M?R5#T*ofJL!!;sE=|Zh-*To1D zD&ou_``dKT{9Mqin$*Vt<*5PBe@x3~i?AB7)?)ZV%h?4HnA>3*_3UuBhXr%vR8yS2 z9WL6|Cy)Q#G2pL$%P(+}k$4;vnc(Fi#+R}(9c2AJ)NO5;ifQ5j!gC64x!HiwhIr~e z;;eqd2GIVZ?SiiAj}&9U3z58KX68GC_x7~N2t6{X=MWe%+S=(NedK{88f#oSo{c7Y zyjSi{Q6bhwdZoS`1$6QAeC^rVu)c@==Wd~@JI1$^EQAF0Wj9GCJtW@}>zHVYla8JD zdg0SzI0pkU2}s!z+u?$}f&G)54PmtRU(?2fly$^}H3h#D`CAE80(4p;nfk7cbbfzyPVsLoaQ)dc5aP(>A=3Y3Ncf9?d8g&HY$Xfp^sbS7|B)H^ z4T=$iva#)n*PSv-H%u?HQA z8Ft=H(V>LM_K!{T(cXcvc3nELnC-6>iI@x}hBkZtc1&zy^ZuhSW!a0}SjVC+-8|6< z0)H6GeJ!Yni8CkE1JX)KpK9C^EqWAKo)b2b>_@%q1pWMJx&^rgEND_jxdY{d!gN=^3H2JGgaQ@zu@w+^kOD)}t>sL9yxba7( zXfik0xn_QFV1@f_7>xXsEdE>`mr4(*I;Ji;8h7%Zn0j8d+c=SVAqTaI4bz*^Oit3Z zK0sU?J7aN>VmzQmaNB|yG=&er3%P*74fh**jU+dWk1p=<)&VtgYaRL_tU*-un{V_J zmP6IE1N!9jZ_8;cI8sqbx_Ty?#oc`a3!AL}>Kbt*J_=2+Y3fEF$)7I%r6I>#CvHW| zTihK8f>lxh;(V#5{2^`A6Eu5gl5;xF+?%E0+*#f?WXJVyWJoZaMylMIp5CCz!(`Rz z>F?06{Pd~av@KR&u&Dyb7MzJYcuZ@7&cP)N;n4X6+3(7b5A8H6)AHG2*c}=@d3ubGreIyt$0CmiX}oxf{InQ&>8VbD={?4+vhzeo0BP4$ z$go({xx-39ScQ5fs=qVmPt`ZQzcu;L%&kF~@m*j|6%z1$StneF)afsK@1 zbY}`;;OiC1>Nh4mM?Z0o1KkUi`_iUM?I6fHt5ND8h8Ua;$XRS^sZ(oQA!rSY#Cs`UJil6!t;disHrj~FU#Tbe8L4u=gTN2{p2`Tm0>2{`w37A?#Wj^2iH{oO7VV35A9rRWxzo#^%1G~wtD}Ob~BM4H054=w(VJK z$;73Y%N>O#CTXRNgzDcA2|ey96x!GiW&XHyfCS!I!UJHNnzKp!vo;=fa}O|uQC8zb zX6tOaOI#U#-_b)iP2e79_or;5hsxWv_>V4^xO8k^De#;8kk(Un{oMxWq{eqOiOim; zMuF@A(yEOz8*F9spe3n#tI5631&hCK&US56#U9l~Ma+{`E$s@|2_M;Mn67K6)gctw zVTV|)5blc{ne|xCcxjvU?CNXP6=VEo(T>&aPgz^5a6qwLM?xL(X@5PSktZTotSg3$ zTrP?Zxn)<0GiUk?!~*Hc*zj!5CfBC)6-tM{9r9hetx33%jsL-=N4tI;@2xkdwKhL3 zBZzuy(+6H|-U)wPrpB-T&J@Uk)}S5e^8@1W)>0eM;%3Q*iNJ&%%IT<-=F$9s#?k3+9T&;}ZnP65uvzAU7|-RYL%=4vi3qRcpb zPx{M|R{F_R{_`#M>F`T2x>iR^mlF#!Be&m+)XDqhkBfG`R_C60X(iARxQ`7B>g7aY zAYTdCW~gtJ8{mhvJE5@U<#^*TEoR7_Uh$W|t< z=22|i!$PqO^+OcYIZvQyM?dwRl^`-A9oS#9M~o0;yzO|`^|X+3K(2r{LsPb4TJCzn zWMb-yo%d&SvqI(mS^V%X3_2j<`QJWtgM0F8#IsT^LAb_0G|h~C9;*r}#IHu*T3m7P zoB=}{cP-B&}L#-EzN&T>HZ>TPng@n{?({jW())h=4RpA?i-9 zWG@8&k(QBfk(<~Tc#oq^R{TPsiq3Hfc~S#NhNjhcfVn-piBeSTNR{#_kZ^1}z>@OT zgA(O9Agyf*$*I$3H`sfBv%p6%{#no90Vw}pX)!=bB0%3(xug5w1P~td&)B{j$HMZZ z!HeXtiO{*6?|Ym8b%dl_;x|m5NCf*VHyL_NYj*s@eXDz^)^Pnx_p!dUk9B>J&xAi&B;^nIe z8LD$bS3bqd7HD2u*gaBg)|cP*5E1!rR*D#~hUi@YXAkIjQ5o!837C$6M3Q`4$I0gb zAVU}&>g+-5-tK`zzl0|L-uIP+t!jI&%8&Z;8)kv*=d_6BP{RuI=AIb+qzzWq@eBQ~ z)Ay6~JL%Z*dslf^5JRt7=ZF<(@_IXcbzxYF<;|wrelPXq#X8+VUZkXQ_zg9?aY6rA z6R>Tihso8HpVEJQd}wy4bbrz%heR-9PF3&vbdA-vM_d3SI#I z9VmaSp-Br$_p1iPCdz+T)t~r2HAM1y#kRZyjy`!N(T4ZisYsdvsH+IeApz;6a4PNa zDZmnIU`*+S3-3m>e0Fed9*ym84gie2Qk!Qjx0;^PiEyBNfiP_43zE7Oj+zCd_gn{z z@0%punc%mChyV}GnG3*ic1b9rw?^3);?HI;38=7wU-0l?BCFPDGaHa(jzD>IMtI%Q ziG)F*yvA6#)ey;z*_{rv*bqfpTrbcajOFf5o6DUJr4TLV7xZFv&ke; z{nhr>Xd|sbz>JxXoY8~fjSSmyL51XJuhkk>=-4k+o@s3la@Pv|GaX!|_Z`+#ae}vi z!_EvV)P8zf|F86lFHQqW7-&G^;ZkG=p#~vO1e>EaP2T3$#}Xe3%Jz5fez1b<0f4}B zff@QHJM2Xr?jA>f8DYsjrMAD`N=eTpRe#H_%v&KNs-Zi9ow#xbsD5Z%DexeyvV3!G z_$X4;6=MGasxx`p$2QbWCIoaGDv|M+#J;kCz3;QMIr_@kRbW_h9xBfs79wz=Un;+Nv{?6yc zxvDC`RYWDcI?E8?KEvTyV(*ut^Z?85cYqSSZ`isS@kuA78{+K&_us_UBmTJox58q2 z-9s5Mu0ZJxgNV)CY(@r*c-eD#{iz5Kh-dw)cXU!4x+fSOd+cG?-Qbqm?Hg7{w9YCQ zV%MO9jbQsLwk1RjJT@7Ms_sC<;?f&{$ccN4?}kRXphmyh0%``xB4DjBxL%ige$bu- zb*m(TlXKr5Pk{ke$WjBUcFgHjTa-_SC)8>sxA_Oc{fpv+KdKl0ax?z72=^yc{#6F? zpMk~y>O+J}pnn6-KsffFt+n`f)A_&r(ElMd_^+*1E}~0ow9d4UUBrNoBsnx9-Ddq literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.nextcloud.ui.BitmapIT_glideSVG.png b/app/screenshots/generic/debug/com.nextcloud.ui.BitmapIT_glideSVG.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.ui.BitmapIT_glideSVG.png rename to app/screenshots/generic/debug/com.nextcloud.ui.BitmapIT_glideSVG.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.ui.BitmapIT_roundBitmap.png b/app/screenshots/generic/debug/com.nextcloud.ui.BitmapIT_roundBitmap.png similarity index 100% rename from app/screenshots/gplay/debug/com.nextcloud.ui.BitmapIT_roundBitmap.png rename to app/screenshots/generic/debug/com.nextcloud.ui.BitmapIT_roundBitmap.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth.png new file mode 100644 index 0000000000000000000000000000000000000000..e27697112456f66d8bcd2b301ae9d3f6cad35b19 GIT binary patch literal 15123 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting.png new file mode 100644 index 0000000000000000000000000000000000000000..b03c71c990480db909757f07007ecb67e8aeed8e GIT binary patch literal 15162 zcmeHudpMMDx4%M^P)Rz_IVz!&<2aWj1`%>jea$$IaTsD8ib{PYg~%`tsUgQ0W}Kyy zoMtc>jKdUWoF~SZ7-oLY)VuflzI(s>yZ5!P{patxx^i95JokOCweEG_pS3<~J&&y{ zOvJbC-zFd+AZ~i)lC^-qrfLC!&1RxofG01G$EFJifHs+4GQ56gXdWFGdYybPj?Eju zLY+c16b60O>slenEK>p`dEm>JE#@t|Nyu|so}BA@IcmQ1<q7niEJ7i-71q6iR zsDFR_*JJ;SS${wN53{CC#VCf`LAGV>POnH}c>4)=EyZ07a8HM3f;1QzPdE;Gq?)v~ z_v@W*JoKdTqECo2AXSwuT*a(OLtO9Y*L7OcXvM3d1h>XZ6IWUjYOu-kni^$eRnfek z+0ot=2Pckd0s|h)SI5)KhGJKHO-VlUX$+<6Jz#xx7j1-O8%>;|dz90f7!CQH#V*Kc z6UMvG?;$n#s9<3%%ziCh{&KGH+m{pZB1|89n8JOkvP<=zl{=P!+x)N|hzr*U>ku5e zrLtAkXJQ$!Tc3BcF4ECAYLV%NO6_zc"-HVbbCtQ%t{(CkO4Z$G%3TsAnzTzz4t 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 Db+Zva literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png new file mode 100644 index 0000000000000000000000000000000000000000..a3e76e7f4b6493f1e4965a11668181d6b7228c7e GIT binary patch literal 16013 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
BxJT!Ic2Jv`%!b7-v|I!!6lCOdz-Nh;Vu`fx}>! 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 TSPc9W4}xwE`}*$2*dPB3iIi&< literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openVCF_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open.png new file mode 100644 index 0000000000000000000000000000000000000000..86f63418fba1f0a4bacda0935457c33fcddba7a3 GIT binary patch literal 13178 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open.png new file mode 100644 index 0000000000000000000000000000000000000000..b32e2585dc8402908fe21b7ac7228233359cd2b9 GIT binary patch literal 10506 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_localFolderPickerMode.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_localFolderPickerMode.png new file mode 100644 index 0000000000000000000000000000000000000000..a463f861b32b7b9a94310a456a28a2e9360b9823 GIT binary patch literal 13103 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

8{e#G!fFk5 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

WH9bzcihnme?Vo7S#T0L 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

~$d^ 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 diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png b/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png deleted file mode 100644 index d98072160630a7b32dceb0b9c5b318731b60b3f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30437 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 Wyd|g|6o>j7m(6**a}{TO?*BK}?D}^A diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog.png b/app/screenshots/gplay/debug/com.nextcloud.client.SyncedFoldersActivityIT_testSyncedFolderDialog.png deleted file mode 100644 index 272d0223a01b34d3a4ace0e927a621d83c6cef6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41355 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 diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png b/app/screenshots/gplay/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png deleted file mode 100644 index 10d5bac2d7e3f55dbd3f0f8ec2964d9233a29d3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30039 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 L9u(et^7?-PlSXW5 diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.etm.EtmActivityTest_accounts.png b/app/screenshots/gplay/debug/com.nextcloud.client.etm.EtmActivityTest_accounts.png deleted file mode 100644 index f4dc21659a40379f43b43ee2696ed5f11b128c98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14108 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 bl=o3;{$0L>!vE?`;MPr(8|ByU|M7nSRJ8Wh diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.etm.EtmActivityTest_overview.png b/app/screenshots/gplay/debug/com.nextcloud.client.etm.EtmActivityTest_overview.png deleted file mode 100644 index d710906cec7b90473f9b72189ca4afb7a6f49884..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19113 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 s3h5KPZmFf>mX}U`6jC~OPWjA} zYLLD1b7O;$ItmGr)_Vf7*iwtqo^VGj>gazR{nP)y`G0!`I>EauRieO^hMKuj zQN3<5{}1gkDk|!brhjT_sHiS;Q2&P(Sl~Z>^t+#m%FOjL$FJ7t)YNVqyUk%5)TXxw zGTS_I+FVu zZo^t&kQ#xcslY?{P6+ByDPm&G8|AaMlh9ppQ~a58_R<}nWu2rHQ++N$kVqQJir`b1 zyz?Ek#~%;gJ)|odP0S97YA-54jmsjzgiOt~yBrU{&sP5Ul6WQX7*y~9d1VxA60i_g z-K?>_y(HffGS1N8%B5NVo8lK5GiuUCsIB*XtFfnAB7 zTT0+I?>Eyglb!E)=o0k5_Myq!*;us_MoMB6G#G}QSat{?V@!u<)Yv5jy<;)>9PvrX zf}z$NG)xqao78!qfA4S{%vB1$os{-<18$nkVu-S8+GSm%%?>7G|tdHO_JG9{*uUFu*VRF^B?9mbCn5Pa>YWj9|!0z z+Q>nE6iUkHY*Aj_t2xM*t6B+T9tgGXpzmSLMnrygJDX0=VG)f%t!8}UO)0Xpj=h>r zUp}{QzLgd;)IRx^!G#i^dQ(1;r|qHs4ERNT{MG>#wCkobzM}jXzNpLZDNcW%2zya! z=F^zuWPln;Yqw-njQP+s;Cpm$nlQ@God+Qk_$dv#ukG<8Hi1 zgIudOdLIS(QjC1nH1K?(6$c(cgiGD3sB|{hz~(#4i5HsI^?L`_RS~6(Wl@%G(W!R$ z;6c$(pzxvDm=G@UNH!@^%SR9w7o!?h+>??`EbINbpQQBtgnh&ARC&d|$(~dNZbMv2 z-pow%HPT3;Tb0_gHhl`*H+PPPdqmoB)t7r~#db@pd{>)%$DwXuVzmJyzwrWY0v==E zSQ8&g@i6g?)vcRCFgvO?)!eVsV7{Y-TEk&%M>fX{x3wjgT6+5B3+K0Y{Yb&WF?43e z?e#KrP612OC3?+5VN$P*Cmj!+fd_xZ$yOd9UU)VhO1ZpEG6;e)we?G5o?jsfR2kqSp!! z_S7?T*crtxSw52X`EhaLE!xvzq}IM}{E>80nt}0Xtt7ixah9s?`}xD3L@b>)_+hbG zvV{4w!Z|~Wt(^=3*lnoFj4u{Fw(07>(3;?Em`_o?zq1e_(ml!HyQ`tAoP=m<=r1cb z7SVn=m*daAHM0HU4M)y{=e2I{7uUuO+oMi0u}fLKTE3iqmXkH1isVPq?!~Ag_%Wi@ zPDj$>!0lTNswHe34r$jQjfb*)3Z65v=xABEGPg|{mF60vGmx@dgGG6bEgQULA}vAcxQ#;I`_=9zY9ZqbywE(<-v<-l(MY?z-ZL)xg7T7C(PbfgeW48R7iM zU$zrBSz*Mx9I|EbT(a6}7JTu+%XZAy{1tQCWkkml|LU)kx2tKbU1^`6a@=x}9~d>+ zBq=orZVvAJghr2e4ywtQnK`~dxlO9UeGiRTQ&f$+HZCoBpRMi3u01+sTtzPux8zA( zuzIjprC;OkS#vqXuU2m}e!^|Ti&Zjrpd-N}3gtH5P_4yYZ0PmsFlM+>4s}qqk3!zV zNxwCyawbK6L_uRaavWVpx+n63Ki%z}UfY;H zL>4fr+;8>?k1Zw*u^TVTUJr?mGAf0i)2*eQ0jOgmR;oUVDKfEIU`v_UL|{}buv!Y2 zjYzhusR^n|Ae+!?8k*F=TJ;CQ7ulQe3*xdq=h)J7)MbHb;geJ37@(2I;u6{Lc?uu_F6Y?(4> zP7IY5u2;AtgKKZ~Qw}uEkSt1b?ohCkM;by6WX+NKKPt;9_K`Fgub* zS^l28x$6SgVRFgbFmZ56*LaHwweP9UmxaIp?z0jeH2CDrg*LpYLmOi6aQTnr6 zpPkrJ99f<%+i4T1=Z4gV9>vlwOsmwk>*)pJ1fQ4LS^D&UeolSIZ`O$Y!7ge^-!Ps! z0lgRL@TBif0{pm2ydoi&6cTybN5S?WD_${$?D)v56SO z3jsbD$PTLgx-L4oJ^pS7&!t?4+uqL3p+VWSrxS8xuGQmB8vM}#?0FKA7h~+3;+>(| zEKXagX=$TOj~V-EmO{aWE*^9gqEUN~jN~22y;TB?DSdV`d4ESEP?!aZ@v(QfrZ=8& z`PQORKFOr~rL#J{NbFQWc5EGQLla+aUChf5o5jL(P?fD+iIg-2$WtRleiQ4AVu3M= z%pbC;Ja}Haz}HPPGfu|^vpTcs#*66pxU z-UrTltAG7Tb&PQ5)B`Z&aQ=sKs`*ATUBhNIV@!5;Xo5@QCqMtB4|Tk)Di*bVj+g3? zt2bGoDs#JoTyx~OJ9kYq>uZt96ybc^ClVmEY%GWolTSH8F9M!}%2ltvXBch?eTXek z0iRQ1N=HnEBD_-cs~JCTLLw*H5jz&H_p`045rQEK2iSAU0U&5?lH_;9mce*HxwGqi zPR=nrjNMpyLa-)AA&Z>bl?&K((DlMsB&E6Bkn1Q!krSIyZ9C4h8tH}dl;BIFd>bw4 z6A^@}Z0>px7SAJS)F2ZqJSHHzNmZTPxL13u7@JWEhLIWPaQd*oFO%y5kH8S7?Bs{J zo(J7QAFJY$;NxnZI|13yX>kDw4>U|rxRVKv_^_e*{+Zts-t6ImdpZQfBF>Ns&Q^Vw zq=qXY4eZ3V`@fli?D^LyO^&8{3 zZdUixW&mL#auS93!9t@(-Wg&~`pw&t1z1Vw?pe`r7NPJ8HKGASESKX#7ISWgxbSCR zXEwdhp5axq+cB|YmjNn3l2FX!uX?vkHGJx3RCHmy4CyNN$)BaFi;}C7$6 zRZFseti&i*jb^VJnJPT+O4a{t=5rG&7qI2ROW2xV+Z}Ir(=(TH^}U(Fxkp9 z(d!|RqIF)&kqtrEeBE|>);nYLD~jEBcV0P{TPz5eLSx+eK(Ripy9TtFkx zB%;&fi$w)zuP)yHL9f10QsIN%SnS8GJf@^c`(-ArVGA{(+O?zmgIynzk5S#eLoZ?p zWwKRIk>nh0FfU*Us@i0WM6%=%2GX1d2grkkrWQ}2H2!S3@aO5LJXQv=RZI4Am2e+F zEne|En~lrsHqtI90cN@!E=I|B=r8f@2Y-rrTORbr*?RE}tF`G_NLH!MjPI{8x9?B^ zBmcbxVs+@($$x53{Nj)Q&>ns9KYaANpXz9vXMeRTe#It@HY*n=A`QKExAnz+n)PgE z)+OVv-N`cQE#~wi`i>0iU9(jm!`poO^4+Gny2EC*c;9WOq@t$$(np`h6h&>b=mw`X zqFMzV^_Ww1OW;cVy=|kH$yhiR+AYTg4Zf&U?u}X5>UJ zpkWoAuS`QZMgv=P0+(;rMOqH?UAJ7AQ%^;986cgKhblQ`UUeTy`$bb!>Ux!**GhSN zz+BN**PirEfqDxEP>G`h&f&>1>eo%y(MKwPuFAKPJ^2_nW7TXfiX98K5n;Iml7ZH_ zRSq{AcWeL_0!m>lUy5cG9lDqzTw&w;;~Tt?HKhte9@S75t6tJ`8Mf@2=Klf!8$zI` zCenans|*UKHAEVLnGML;1o0oVYXIXW+H2duX2SL25uIZVmL*BDKEY~=)myG`%TqZM zwQl&r7qy+LNfKzG#JU!Rem~6Cs)8LdRo=Z(CSdy0YNeAAvh_%*U5c-Qxly=C#Lw6= z572cHlhSS{LaFdiUm(`C-zvA6*{$9Ok5`QQ%ecXkv|pZ`Q#a+ec2)AZt{Yg_Q{T5} zU3b8et|5j;E=$;|r_VktS?K4DD{QUNj6Gvt^n>jQ_F4z|X%wTWLXhmW*3zP*xcKc3@a%15Nyn&?LL$ z2{cv~NPVo0|Nh_+-XpdAz=h;5NAYf|2`3jVuZ1{{TASmhY{kBYM1;wOuwfU6y=wV= zrtC(RTdN^QQVLsX*i?M#yPS_JS(IP(75m7KicD|naNQ_57{WV!B%OZdP0Vrt}FU0%s9|*1|zH*u&XTVA&Q5?I6-^?xVm+iMxXX#q*c?)I+HndWAs~@aAARymCH`|!@1Q`-R~~ceu#Z zk}g<+hRm@p+>$H}IWV~zP}O&7uXwyYCz?~9Ad*Z$H}`q$H`woABKd|O3vbqNMflRW zahj$1*H=m2yA|O4^cHHksgY;n6)IsoeY7^Mxc|!rQS!^mW8TYN@}ay_J%PJ}Nip3M zbtS3{BG@GjiE4hIbURm*Dwomo3wN6b_UT;)OM8DHo`WEJH>PgG#n`Wf_FJh3^XsmJ!V1d3P6VsyipkcF()DFb!@;2v zGTH)c=Ex$k0Iv;hphyB zDQ=t!8@w#K?+Vydz-qpf=j;ixJp5`wMWd$816Yh!p!ZE5Up-3vQWHEn1M8AQ7TzBR zoDG|SZFsJ6GS2BcfA&-IBL{ru*$KxG2Ti1Xp<{q7bzXGI(x7i!0C)ab_C<~#syBa; zSYWKVL?+7jdA&W;MaeMvSS+BOF7-$1;}^ACL`_tQqQ|;AReQ@B;a9?HqgEwD#cme?-}h0@za? z{egS0&lZ!FwZF}l)wTLy)+uup$*Zy58MXca9Zf86=yigyCuz*ZqWwEPQy$7h1;a5< z?!%&G)CUVszzOR$8fx@?UnfO zK3Gq%i$pr8B3SNlN}EnG0WQe>{-0WGd42muZ=xkdtd;lI%C&CuD}GSB`jG*uG8Wi) z>542jLzR(9d#|-Z9J0yY2qSW=oNsz}uO20Q6eyp8VlIcm@>EW2IIuCP=}#H!#l||U zYp#CI3D+GCz1M(EhAAkE->uWHF~+8m;h8BG+*GS_7E;CdAATlmY>xXdHLv3sKofst zQ!<6#T)HEk3t>u@z^qcMRi*V++8x5AiDL_NTl4{I&*YrLZCG(Tyk~FD(7Ut`8I#TL zF_kYK24L$0b|#U5dav!S>Et-(zHRA0;ACSFUz6?|^B`Dm8K}{^H4E=}x!|3VAcG&5KLrG5Dm6*& zIEQ$FhaF@4zuomPUT7(k3mKOqw;c?vF1ML_x)oT~$Q;z8oOJ+&xn;HU-lco@{6`#+ zs9~{lW#iDh{>5>_Ul>1AWBv!?`DjG|4;gt}=S|@5_DJH0eW_`rL5Y^2gyL{mPJt7Q zdJtPn@adK?O?vcYOYRoKy}~y>o1Y5Y2RPh%I4XNbCD{|tKX{CPlO*Z45qdrE*?5N1 zlKnO9oXFh`amPhACj;t|4kdi!PXuSFzE6heg?;*!n%pAi%?~}=AT^j3)^WFEy$O6+ zOl9A{9Q0wI;d6PiV|pl2?Eb0O-WHk+yM>-xt1=f~p;s<m8V+626P%q-sE^12egyKBa$$-_?=aW?TTVG%Rd%} zVaPsk9Y5}q`=A-FY>!=@pw2!+uXe&lIv;aG>XDt8Hl0{?hCgFxh=--rnlxRPBM=YM z6=e-NbrbrR8~78KbtEQGBMYHJ<2t^y*mpmDo86a|Ld#5uZDd}R)=7!|upv*4PqjDe zyqE03&sigm8*02JG-J@XS(m6))fj2%+lG^^xQRbKFI*1M~K$m0@BF zZuc#|_D(hz3-jxGa0>iv0ACB63pG_eQ{DsX_FT_0%Inoj(V(t?Oh&L;1irqw2EKw`x5ViXb4IE2 z1em4!?OrH6bT)(m?rK8-%Dpe6^%?F^vLhc6(Y(&9FZb-TKY7-ASW;Q$Fk{MUfO<|| z{e{yfcW3jm9|-as+Qbe>SOv=u0pXhsdh&Sl)v?+jfWgVenoS&X0; z5mJDzUrkNi3=hq)JPPgFsHK;dYN~vdHLL|rN^FM4ml%`{sf)m>8xeAw9`T4K_&F{4 zIv08nH#{K;0r%iwz9T?iUwzM8+OE&n7lCE+t&&a3?^Ny@jADvsgkKzkQ4OwgNK94P z@Z;Enc`ZDhFDcayLl6@>lZynO znVcwnNdR7dx+CfT&CX8*HmQd(T_EZ>XOD*bkVvMUTbF;P@QJA$J}U_*F>5DRn4@Z2 zF8fpGn}oC1YtM)B1RMch1yRWxUX;%ow1RC#~AIZ2{1g0Pd53DQF))@&$Wo9_<5oGtYiZ z6F)h>PhvCr=>LSzhN-~pYt)+~7Gh7CAF2#tklG9;DVX1!1o2!G*RJb&EUyqHej7c! z*E$z#5%h;F&o*m{Y|*!eSbR70`%zu&WrV=*>Tlc=ZisZE}N$h>`&?+F3LF(X^8^{Z}&Z~*wt>x(TZ?!c?ZEW`Gr z&VTGB)YI4HmIC(@74ZN>2xt({M(33d{}i13dL%480g5N;2?tb0ab+g_U-o~J_)=EvX#&T{<>BgNCyYfH+k6=?`2&s2eP z9|@GNjb$UXn~!u0e2oqH`Ff&=^y-o1(LAD=;`F0ZS?1T)B7DzvrhLd*IwP+i2c?xnH4Evphm3B2 zsKn0O%Ra-Z_4$sX1Pd=jsyefnKX?omem@gfp|=)Ax^!>yeD(Nt%7|aY?YCzHJhb(FA9E%I$>k?Epz1vwDlT8Vgxw z1Olj}Oh!3SYksur83_Puh+2607aUPwHfl30NP~iOa8&U>9E1(7EtSk{p zoo|D0IIOFN7lPR(5{xg_lm4)-z4Z&`pciq(Q%5#mIJJ7$JZXgvaLitcSC{(KH_y7d zV3pI)dwPR^?ve!eXhY`5su79E zwhS(KC@-@FpYCwgH9L{I(w&}qN?%zlQLwY#MT>NO85zc=r?t)qP-a2RA{DJSOx4j- zDzW`JSY*H~pKatpwvOGWH~n7~ktYfrxNdR^b{aXrdu(TU>>ByKos0_}Mb3|xn~u3! znlEVe2-_)0FxRu4d4PG%pU#E_U$T9!xJ)d4NuqOnK!{H1Ne6;IKxPBcxlbj+bV{FtOzkh@=mP8FqagP!=?xCxd|XDAakfYG~Wg@IaFxGZ7))( zGbc`pa=$EbG#|EYDvC2cZL!9Oqy<$i*cH-$9FqYFab~x1)tb!%tZ!B~;KuKn!`nat z?zuqVdO2l3aG1ko}wOqQP(~C}y4k1q@yQ`8ic9L}Z8oyRC zh*$HIFv^nE`J`HF*}xYFx46y}`=c5c7K)tRS)>PWwtvm#?Nr%iH{sBwbxXQeY$Y%)k9c(pBnZvTB3YbupsO_{;G{09rfz_JoB<&5 zP?BhMhRoQ2Wc8Pt!RINx-709J;!n$Q=6lJI(cdv5R+(4Q=kzAoZKcBP^(4hxu1?c{KQ`$JhI5)5kH zO?i@UbsC;pI3nFCB^h5bq59pN67{)g%V2L-Q^i(98U@J_3pA2Nfq1uPOh4W|0YHo$ zp9Z3`n)wBPA^XgjNdWsC62_0JvoC!5g{l6o=K0nBQ)SWbilcw3ENYKFeFV1tRU!l$ zos*@q2{^w_m+>Fx%ybUCS5vr_V|T& z#=yp(Umg=Q9s(uaw^(nvIXj;@)o4%I;*bDx>-N+elop{xB>%dX=BCBgO3`Zfrl9n? ztj0TA;6}Pi-_Ogq){T3uWW~nNg#tO)#071XNP=ViA(An1Mg_GgOdqhfswm052}Fz$ zyJJAzPDo4@G|bU;p=D)9oTVEN_L4kYi#?P${JEugJ6h3T9Dv|%+(JjbY6Fst^t5O6 z?tLWH(V+5EnbK9Xc|a|Uq%aIAMw%764P-^ck1ApCQmapDQxV=C5%Ac&Bn)5AOD5en z!@{+%S{o~gW3_@VI~m6wsHU6jAGe%RUo&Pm48x9y&%ur|C;)gje22W^hz*mR{rM*Q z=4$w_4PK-TZK(>)KlM-wK>HPZ1rDmc6@gqtXLJr3nyMI+F1x!E8Xi`fS`zle0`!*PWNZYZRJmcGD|aPW zIP&&N*j=#AK7TOqKQEmmyuJ49SPZ<$d88UGHCe#cCS2z^Rkec* z+%GHg)!Eeu*ekF082>bl-!%X zkl}W&5cab5_G13VfUdS?L$F6#+4im@c=R%T7=!IIguup10f0Q){9uVC9v+oZuqt}} zXkf%)hFR7k0C(?vfzJiHFyMZ3sgV{V&q#BM`;~+1G%JTtNatX3GGX=u=qenUh=5qR zxZ9w`(XhJ-ASh-BuBi=USryG#Z*#fkKvQ6{{z%$?IRDG=6Pss1GEXux-QIy=eNK^5 z@`9o#`{&IB1^a@V&i%TUv&-^!>HN1$syT3yhSC)XF6_Skp&lqlMbUjdWuzjYyl7F& z;QLN^S%r%+p$pMeGv(LIhpF zEC!XKOS9>VjlaZSrHs!&Km!H8JT@$9ny7M0*)4xmE2y0$Y+CAU@9>Rf)oZ~9KQLwp zBlHC`u)5&%j%AXinZ?9p#|we-2x}a^+NGTMIxAVukUGy&0bgAssh*k~mA%+^i%)&# zQQ>r1K@VFP6mX_mxJm(+%!12jUer-jsqDPfGY8Uu;m0A*u7UC22Yu74QdjAGCGMLFiW!c}gwJKkhU9yfAegb*TEv#$OVem~n}`U78K(98 zK8AuTQ#UBNF+g@0BT8mg@7ygJi68k&U zg{Od2546CJTCbrat2>Xw9{ut9FTK2T=es>M$-&-`H}bdUmizat(f>^<>32fu-`f8< z#q~SswS6A2I{&`v-+TT`3jAw~URYfcHgal<;&h~DE69i(s~)WH}tQug${Y2SdP1E03Gr8 zs?ybCe@=Mu!o>^s{;6B@iRP1YbuA)*6*(4k^3Nx&CoN^xBwhC(!yPY`=k8rq0%myK zq@+uCX8h9(gdj^pdj{ACaJ~FD_;374>Cxls-xNmOe|!J^$gXnac8UY8qCS z3$c7^B3s2nf9-PnMg0A@|K-;HPH@Kij6ZjIr3A(EY>WNsU+4DU9@W3*n~`pxys{P4 zGK4lHqEqN9z_e$M4v_xrcKo|v#2tg4pgr?zet^I396kR29GVSKLG_#M{^#!3z;*q( zy|Wyairj|Qw5N>)Q8K`R9Myg?oA);>gL2>ZRjwy9{pJ+V`J?1!(*66m&VMnTUeF7q zjd9McD;|Gw^Tmr7&;7}KdNl}t9h@mE+o~r+?IncQpWN|RX!rN5Gmt|50)%z{ITihn zIp*Io{I^W=zbS@*herzt@Bl84w0GEQZUba(6VK|*_}-rW0v6V4t--|K)SaW zGlO=S-S<|IkS-pRV7X-^b(E(9aFQF+k9a=awRk^FO<#PRb!?Ww5DjFCY`WTrZ-O4K zrtNo+fiKN^$jN_K*qw}(8W(0)^!n_xwCVj}0*E?r=3^6tuE-@$b+2xq(D zyNhY4i-xvyRpVYGkp-<|9C07l-zizJ->!aU;aZMLv)v_gC6b6eDR*tb!+@vJ62EXp z7bMex$k&;fIv}7Z8RI4zbuJ@K4IxA^Jy_`+N0iRVUmgu`V z@0jLSRk>r+W+d(~Y@4gKZ<7lmN#zX5`7PGN$@AJ(TIu(xpn^-6Z&V$F7M!Hc<5S-} ztuiDmgU7?mAZ-=k?$^;Y$v`1Nr967@QcN`g$gg(Oj4Om0dTsQ%`F&+c5snbXS3(%E zcR+)aFPWh&Qf97kE&nPEF|fh&knj5COh5GjaD<#u#X*HK%cYg)6D$kC6PpmELzzc^ z*e4=~W(|Xgp6C;&6V!TV=+(!Qmar~PJ*l#$ym*dHXtA(2f$^~{^{0>y#EC2x1{$*X_WqHEt3?9QEfNYu(_P&q_P$7T+o- zewOF>pzv-cI3G6s`{T>u7yc5cf3mBcy^~=j6Y8>6T=$fDg{s8WXn?}C!O1@*qBQ&Z z;H#{v1)S~s{r7`lV+IBt0Ps?2$YUSL43=;{twb{u^l0qx2kEKT&nX>CIm||jpXF?X zj9V9PfM{!!!Amm5yrDV}xI#-Pru4xoij>L8sx&pdkjqZ)crxK>h;(vngGlv9ef0)P zO5~hkT=sx6uM4jy(ioX2`(^*cWh0-< zDQ(S|(=$N1N4T)zXR{hk##^0gLbWJ7hMryG?H8z+Eh)HY)}WtZqKx~i1wdei--8D} z_J!YoYNMBj0PJEy?L9py>c_-##PU_{_?1uYsyu`YM6njOFQUZe{3}hDZC0ZfPPVYL zWkR1UVjxA)@B9{{nd^+bE4=4IA$tW-uvTS!A1e)$`m_}SK@*d`Pv89}2*9#LRm}bh zLuEg<7~ALJG$FHK$aVBZA1Bw^P(2Y-OI;B|=egnmh6Wcb^>Ic-ebdb%@7{o;%88^L zIz87T>e8saj~H3|(w~g1F)i>6r(LMFr1$qVA{!fMMh^?5b8Kc5yyj2-q7K(FUE5&p zO&n%7tn%om#bmf(zNM;qh$HRD$KTKZk+ykRs=e=U!&*NLq?@kDBq|vhNudFNwr$nP zhGO|;r)C)Ei-|>gPKmn*n4*bPy7=A0ld`oe;fe#T>s(S zr{Dd5p%;HgGJZb;X71lR|K9Uo4EX)(7Bf_?warMl){&NOC9&KVoUEPal4RK)j*iK$ zQEu-m>xtvgZXc4FH~LkAb;a3bVy_3**>M5dQ5lmHrn+kJozpMs*H+Gr4AY8?i9U{I zo=Yv`$#NIs|HhqtWT0 zV9+af_j!ZPCAG7FG5(e<{$F1Um|DkaO{kO@5S ziW95;yzKfGAmHP5J`DomC3T+TQ4I20pcQ+KVfCZp2{NH}Ej8&ju~fhAyT3Zw)IWO} z!MC;CU`6Jxj+f@mb8J5v{;YSeHBa>`OF}YgFwK_Bx%x}}=67EA^_I<-iX}^WtHbV# zicWg!8aeXe-iY0cqPUP0~n|6;6OXx+RPR zd&G@QN+cMHpov{KyNb%>jq@s5C2gt}6kW4+dQJCQ_sHHFiNg4vwjoNB;^8P;I)#$z z9WqR$W`L!XyUTg6NFCjX&W}};OMtBd@`VMg!uUL{+@%#!KJ?;}kPNe7m5*U&k{Ib- zlFU}MOyG)*(@tC$68$zOuX}9@*ImWQf=)2y7A+V`6O@z~j520He_9A3l5N!|-l@p> zZzIN63cyh?%l6{9q^8u)@1;f|(PI*U2P&K-MO2A7c||x(I9^*XxgZ zDi%0RS7S-v>yB73W8me_NoL0@*V~DLo3j-T<1&tSck6I`a^0p&-Qt`CeehzHfdf~d zP3E|+{o`npN{Zd?Ix<(6)GB2h7$~)1<`g)U%kjA@&Bv|A2eEUZZog+d!3ox_We=N30GfO8W(ECJqXi z8?(;<0z1<-TXbsZUT&{Kyhm#DTMJB0JFmFy_YOa9<@8%sczV_bzMje2jWH*={CTxf zA4=Tph}XpEhPy2o7P8bU4GCGP2sEvG*)9=aAG9#+46X54i-!}eVityM!^g?9m36r@ zjs>ce;w6BxmomPg-Os3{moW=D1B-)>mUvt}_0}+x8oFe~uf?U@V~3D}WZ;rBIl@_} z;u6#hn;F!<`Aa#~niZVelp>pr@8tTSirlF42vIKk-hSXR;N_@WhCXe%1!0(+0=Ac{ zgf1IMheDi`U5g8amru3<*~FF=mE;K3zMAd`rFd*dJh%5;LRsN|)Klkq9#W9eNrzj} zsM~16dh>!iK(u+5YGC@yn`@3&EP0osR}}*Hi$@yfK06Cy3_*2B+z3Lsp;89whXR*2 z$@kl3?xSRj_>F^SaemtN4kw(jZXgZ^d{<@D%sP8@T*29R2S@k4gJ?-2dopfvgItBM zN?zev;`1YAhh2c4czv-ENE7OZpDp;o+iFyPP%jxjLZ{wBSRed4kh7%yHlG^VZ|eua zgt{eM>^Y{%PGhXB`gLaj*I%o^Hma$5Za*>VO5PxIcwUrdO^F1(`P110>u>4H=8Q{HJ&|7$U z6b#lYQe~4x_)yYNN8pfoDH&gE5^qqv;qB{O1`1nza-ntd4+I}};o(x~mIA?{;EJ;2u+buLOg5C7kNPYur6Meru|f0>)sthM zi<*p2D^Wa@`O&23wHH4wn^M+PFnEjaK|h{P^v-yxAa;TGRqe^(!5Ra ztWKHSPWW zoG|>_r}_2qm%WO=zTBS}>C1#T%?(}R@GQ0#?YRNmx~BfmZS1o3w&);oA z6u&t%MRPkgnGt+6B{E!ll1znuPkL)r`viYx#DDONY5>kkqDmtj7_h%_RGd5Ee(zwN z<0~;HD5WOeTzwwzmIJ=Ar!o0zZ`Xd6Gi*3=0)fK&BEw0e9r25Tr-s|%xx0~N{s?Wcw-5Orqst99b&5X>@O6#}b^4J>3 zvtb`!TBF^T(8U!hVoB#ur&qo0_eQna3q_tas-`P--!rBYrC!LZkyxdjruU{ z9cgMt2hKcQX$Xd1vg>OtIb#2O=P*0^`TaVF99edfd#w6{-Zd(T!p^VswT1!qUyT>s z!-+%5AGJFS453LKPYJ~&o&-7nbroBfsm6zcjEefuuDXVg!AHx$PaJLBx8vw5krop@ zQXz%e+NTa?f0!mrW#=QM+0kR{;z{&Kz=!$= zMmtg!_^ZCN#-zT+l1V+mlg}tB&IC(LfOged4}S%uUmMFkOvHT3bL}rrPUDzIzX(`q%`;-@}d%_sEHByyLY)Qip ziJ+VfuKBzLZB9OXYJx~Nvv?cNhf&9oN^u02WvgM14ub?CDznRWvfbg z%&^(}T#CGY-AvXEPHAz5L|EwH&e&A_8=r^W2*2b_Jz3j0#kx>?G4mkz*oWp5V(af2 z>H`mb#C5&XH23X5PX_F8vKYn^>fW28)~g~KD`8z@clOEXh8$$8PeOB%^OqrvS`1#F zN-S`f8219Dy59FcoSO)}Uvb6Gr#DxBLu}A0FHykKe+c3eKU2`31F@=Cq>8o$9FEvs zjIyYeq}}?-iYbWC(+R3;^2;zX(?03o`Q56x8jd|1BT>6q3SsljxN})Ot@|)IaCTE2%0kKO765S|ke^Bmjy71$3C--F(jn`YN&h;$~}%%_iZ)~a_4%e#GHhq zA)Qd|mcDmilP1fSGp4?cuE`ZS)`u;Hg><&7%2Xo9C7F}->pprt*+5+BteI=u+}+>| z!?c)}SIZmp=MIC{MC?O24BH*Qh6WT1S-{#Ic3)n<0zTQdMRg;_-C;fw%8j9_T~G$MZ|e@BbE1#3S>M?$SV1rK<9E1$Okx=Z5GEXBY^i zQQ*Fpq^{~vLHJ~&A&l%eN_X&Fz)N*kP4$e%4wxQo)77cp ztDIvF>g#UOUxh3F*#mM5?U1pWiucFTZf{FJbJ!md5p`Z{Gswh&LMDF(Hh*&$4nwF7 z>piY@jXC6vgopraCt(MD@|*lsLNf_X*xo=5%Sh>}@utoNjI!1|kyfC&_ew#HI-W=> zY#`w*8~cwAVq^$M&SBEpBGcd7qlQ-+M$Ix^;B}Np zf~MJ=NN?n|c;>0$a69GUpTnag)yu=qapx1B+@WpG&MpRNi=KvDld!hvV4A{PJhZ>P z=dbE^wP#ccUjK;WyYKzG{ElfN8JInSh}OQ^GdfZ;n_eQ6+2p|zQR`08knC}Rq>hla znT@PM{oWAi?AW%|@1u3eQjOxhPDewhGE4qQK+Aoz;z@1Z>XoW>*$T-Nmu?GhsH)%( zkvt2{Jpzq#7#{6WtmAioAaKP@Xofmu`Q%4jv}qHp*){~@(jFD%5LejXC>7So6US^~ zZ1ZP`)`HT{eDfSKcz@RK@~0jg2^mqzn!h8Ckp#i2+}t9?&VX)I%0!>~L0TV9m|G_* zpQC)^jhtHY>2&Yw&fsnO35FOY?wI|v@vjITc(&YZxptp zXUJ|il~WL5xxRA2OEQnaR)fPd(VlJ?Y_s{iu25qeg z`7yMXMGyIcevmuQ_sv`*%SEiS@L1V&2!9HiNNJruF2zafySw2N;ykN%d7-N=n`(U; zTQq#AZyXo>I@3^y??D@H24ACn{nU3BnD@nb>^okwN)BEb9HzaJ_tZ|c9rm#ziDf0_ zaYJn_xM#Em>f#~C!VnfsmVN$$f9L|YV9?yQ4!6WCdp5;tx~L`O^%<~XjoGSD|9JcR z5!ov?cD*FavcU?sFhh3nggMjTRHFI(Qb5F5f34UsR6|wsLxJl6yf7&FB}wq1XP~m+ z$UB%0u#tms_F)<)yDD9Ty%$4>+_{vHIx1NMqbdzBS>KdHQe^GAt&gG z^{ZgF4ND#Gv8vcY1}pWARHZ|)(Nxjdte~#GjgUuUpmL@TEyo8XEg7obF$TSFt;v{H ztz}LgD{|*_5!B8nzV)+sDmJ+fBPCsqEfA+becnE~75)-!;hhs6cmV{8JIfm9!g!S0zUgI7wK6ovrGtEXrI%dWhBv<_@leeCW%gJRXT+HoUHg zYG1w<)qyuTD1$Pm2aapQGH3_lJu3eD^WG62;FJN9hxO_-63`>JgRr1>LNYiyL(diQ zMy?Xr)!{Kztfwg;s<{A%M}MiTUmHg)^_unPo|t23$KJe-YtpYtb;L6x!Gr9(&dDx4 z`8lNK?Ux^s%@TQGj$p$KdmRlU2T!8h;^!8N4g9ULC(B4LX8)Bz7i{s6bUkKjn%w4t zTXhmo)?Q5%PObiEoAC_XK+vna!o&na#V}9l>l~u1Z4pH9k)8gMuq{qmO_mr*BbB=? zd`9zfQXeZQ0fau(Y80=uSIrQ1`^HbP!h6v{;LW!t;K9syliyqBQu?nBZ1JT=>CJ_z zIKSZSg1&a?`K0~6!3A4g&JXXt*bLtlJSt_RI@Faas&G=V^E7@q8Es9zel@Xhy%rre zhFT9u2~0)&ntX9&c1vqkwn6d6)|N*_z^+s;#&^4wf}C_;@;SM^K9x9QfYKo4zN;!6 zDREHpbsebaPxY{_{jLb>Wp9-F%;6@l)5AjB8%)Vk?#z@g3$zvc-29#6z`pA^MK>zH zU5s)mGe|PWz68<_u7M}>1^GubWlwI7a}wl&{T3pX5_bo@7WIFSs$tsW(@ z)tX@3w%;rUZgppo+`v;9)sbVr4vxFbgerRlm?bs{=3-VKSVc37o*{o5DUm}H?NEZ_ zzEa(Ft}0vadAs~QPi3(4pKKeAIeYCBDESe1ika!lsAS~}W)aiw^`yvMxoAeyS34}u zSMG6(Qqc5nb&|NvvTSAJc5EUs>0(qvG@n|BewqplabSOeh=;(i{@a#Vv{L>^wT-KX z%~e^!^{v*Lb*?%xtUe&!L{X>s9@U#S1*r6s*2~g?V=~?k7qjwr z%!i*>MP6006;Hid+G0~R&(C_Ci5)%5GcrEk*^!)t)^f2=LOA9GezQdTgy&10O0zMOn}i!RMQUy3Or=J`Ml z>lh}(f;kaA{QhxdzrA$W`ru*>Ei`;jSg6U|AT5PvKvIgn@`XvV61P0DR_xv|km+G6 zy_x-JU}Vid{-qc$VJyzo$y2^KVYuQdJT#ZX16ZeijOA>RXQXPuLV`_KirQr_rxqs z(W2gzg+t)x&qH#I)@Sw;F$tF$>v&}_?M*p&s}Bxdem=1tK}Li+Q;uV)HHwiqAO_YE zN*%}j%6z(YE$#4$eqAI0f*V<<{F63(P z&W~X~i!RzG9GCyYKD#3eh@_Y_aecDFgNlIbu7V-K9G`N7!x*vSXO-t^sGv@4I=c`K zI_~5tb%ueer^)e8s+`EdhM*F^U^_lFWN2Xsi@h~>uChupv5QMjAF)1ckde+fg!+*l zj46^$I|O(UU#yxV+a<0Sz-A^Dt7_IR3->g@;Zg{$jgcc50ovE-wDkG@)Vp)S7KRR{mv{4y6I3%7q2 zL)pWEg1skyDwdTLb`4OVH3MRvwk4npsNVvZK&iFQxKjIAxob48Dzr9m_Dp##IsNO7 z+%mhzw=R?QJ=N-vhZ`q-&T7nfP;?3WAA1~ImE<9)aa2&LLA&3nhx_}k@ejeimp=&| zACK}ZP@T!EJp>@X9xd3v{eHb2$T{R(NXJimkThx$t}k4=<^kF=cq&IR)q_kw`!K0x%#K>h8x8+xOhXw zOqG6@81q};K_H5XIVkzhKR~x{Hv3xQelT=G*p?IV8TGcWxla?HQ_8n1*N%(}4>_{c z*_=p5*H?H9d)O?lCCUmBU?$M>=tH>pSVB4TbW^d|%mlacP{j+I#f|c9Q%gc`IUoE$ z2n&(X`UeaxGT<;m?@YNa@}3^Nk$`Rx3TBira|fbd0Mr8lna0E@mJk zGL{lr#&Vd4i8-liVaWBWNkYm-^p|ocgEWh7uy0c&&`d+q|hrE|OoToC`!5@yEV+#0oSer=HP_ z^&9Gd1YiCPEXMhzp~23JhY|YPP<3?UEAr3htdBwVGevixwyxuM^Zv*w_ypS4Gp-I& znqBm8G+K0}c&RGTj(;95>Z{AC-wsn~1#-AXALoq}u``Y`%jDY-<-U?_s&VwJe5?=N zsA+U<*|%1yb8zuc|0B&b&D1N7zeFr&b0r;Myc{%?)CkPT=gvX3_2qGBOdWr}5Te|- zI5)g$#vyvqZ2L!~Bh(XlX9+6~Y-D$D9wvP}((4XNXBFUji6om=Y<%kXoUK`Uf=ap& zp3Exm>*+bik^`V@<&5b~(MzD|D+Y75TPt(#ApO1K7)Gn|iPz*?puuX#pw%8fq?bX{ z=!nR{AjIRTOuxD~JwmC6=XaZWrQymaKK*eTfr6c*Xq!54eR!V~4a1=ns|BnYB~HTe zTb#zOP~UpQ?!B|yA$CBvWAz~4zNM-3rbX@cU6Rgnc>6jc&#VXZiAzYK4k^+9_@epY zO!76K(2fA_>%%(9a|9>uq_!2sA&Ri+)Q_aIZt1#&irA8=w6;I5{mQ!EN_#2lvi?Hq z9XO&J3S7<0r``zykJK;iWAFb6r2qHzL`8#(RsaV)=lB&-Vr=pG>K!$Qj#FEqZo@w% zsFx++JA-Py+qk}pcXS^$I4;k<o(v z8cYfvLf{xh=LRn2YqGFc^v;-J1Z@xPxW0Jnv!2CS%{GpEQ^Z+IGf1kVw)O4O9tT%% zGN34@2-|Jb<>?cO;`!2Bk1qlV7-;Oq!YOGx75|uUCjQuU5wq({*r6D zI7b)CMPJ4-+TYs15cyrRw*B5UYlz4}+C8Z2)+zkbJb1zn6g-^w((BC=rYE1ixDQp5 zHfm&K26AbZ)IW0dPK!&(Kd)mp9e51x+)7P#BSJF*}UOl^EB4DR1!AOjgI4bVL_Q@2*WEu`O(jlK zYS^M4yOwh_X?6})^mnMTZ$P6BBI?#;)|{?T3`udG7;eg+FzO355#H(MguVU5e`Gxxa%JQ(dosG!K(66#volRrdVtD-c!+~&h%?h$!W~n z{rNP$@|&x$BAb$dT$dZxMRRE_`l|^pe>|yQ?P2=HmuzlStt5g!K7}*cF0=&B?y7dF z0?8I-IvJu0rVGplpVNhZ)@+^fwMln~rmUu1oIPt4{rkz%ZjtyqwiX{$G2+{ey$hs9vDjt`!K%w&Q(ol3qQ_l^ubN<)Ky(mS{-xZfG=I3vr}>s zm*O;4-#gk{Ef;+pY41k9Sx^0H72jb3v({OGj$BfPyqA{YNG9dml(q6?E$fTDYY=MN z(4xO}#WnAOHGWy`8R&gV^pJ8&E%)L0v^SGjd1ck^YJ{#)7T<9jqX3T()$4Nk#6Gga zcb?Q-+wSUx2s3%d&a)x&2BI*NhjT}|ECZ_P!y_Ci&Z^Pxi)9v_H${&H`D~x_;hF#v z+H8Ft)B0%@*vfg6egx~43Y?+!nvE^R?+U)sbL-pCcDOg(G8M-PrL*t!H@1%ITmFKG4KpbKe1q z&kX7W$*0T-CA#ycBF#p$vNh<(Fc23YVO`&1bZEd%U$x#NT*^jDv9=)ssF#=jQfPAL zq<~18oWkPc=XMtp+}Pf5nU~iC$EDQ??lNkL8n2wNJpld6&Opq)MUmA;vh1sq*Gh9o z7l^V~2|13Q8q4s@f_Eb<>XUE#umcAbJ>uwOX!)0W#nT1);ZJkbx>w3k@`K;C?Fr_- zdFdT-vI<-mkWPf?tl5h+DO1^mnCyc(;m^3ar+=xD?n--+USKGU^y-!>AOB-QQ|fSq zFcW}6<134J+h{MdBJNuwv_H7TIC(CwD9TBkv^Gs%HS$UI;ph+$xh%KCEs_v!cweIOQRy3$R`_0L)&ku(zqP3-+6h zSX=PTz=GUs`B<(-)w{qWs|w4z0r z_m9%rUrd>9tV(;C;|^`3&+Qr|41f7j{sUu|rgZv-%y_UchdA8rZbC6~x6gglm2GWO z#qVdH-5xG_$4sm_8k0;7XK%X>P_RrLxISkjP$?eRDpvJ(APp6iUC*HveNE3}V zg4(>vVE5gfvVwOvZ>F8tVK+>>=kQ?`GRb-yf^uaCaTPeFJ-2M>dJJ;>0siDC@BD$b z7N&Y+*50ouFHg6;AZjN-|07!k>mT)ZRMQ%0Pby*=^+AV3)kmzH9<7}B+xyO!>Xz3P zUyS}vG(5hT!iafyIiog^*T+C(Dq~tf>L@61n}*bv%K%g?e#%*m(JHySNn)Q5YBx;* zUp8ks1qbD_1A6w*=ct23xR#&?O7j&tsMOID=sh8s=E1+t^Lt_n2&V1{uON|1Am~xrW^Di_sOq#(ugIREoI=}szujgB!5qx3Fl3?X^oF+Rtw6@ zIBU{8xyfzZnBTSr!>4NSs-XJL=X4-aqwZfcC2DtdnYc#w1qvfLAFXE`eY7g9ulA}o z*DNO6Ph55WTYIC~>J|7LXE0>hPMg{IIK?5y%Yf@nHT~G+mZ)g)^-5P~Pvos076j79 ztKWcWCeyVwKydpI0Kq+)ZZrg4pS@^xkOP1e?5hE4!QXI&DDP0RUQy|X^gwb}tf(vT z+zRBV0`OGyFg)2c7nylJB1N@Vbet%h(k{+U4*c3f;{HWWU`{H5oQ(c@f_YKp(aZuH zkl?2BT6L+|Xn0pvQEAjS2!1@I*i{BECmGi9p>_Sy)LlKWTG{^({bLQEC65#}A!@^96_J^wZ=ab5-3z{u46~&UP)#3QqOp2(xRrq&x z0GvqqJ`%8fmv?R?d@VOg#YoY8l)-Z3{vZ{d!9LI!^HDU|ds5K_*7Cuyx}SJ}@! zkag9nS38vblAleN)7v9rQq!~n^-fZT+boVY1{g%rd3s6o=@J@^HT$`@t42G3VwCI zr#v85;Qd)2kL4AUz|8Y`YjR^fw0qKIQf`i$u_HUv1Rq@tDF<& zkEWjO4*~ys2&X@dCB6?X3xS-GOGi@l#1iPZoo0zOghboopvMa@0p-i8L*A>C*~l@O z^bZwnUBs9~XNm@qnb#_rKJ%3?^-n;0pl2a|^Y0y302l;h5A%lQ1IYhC@z5z(@p6YL z>D3tTWaqle_4cUfmjCLH8GesuQ4@0^VqM@fmd9LpPGwt|w zwB|1}XVO3H#MJt4JZmk1^~9^nL)Ffz`h;~{$HOL_?*2nxg$?0p1Thl5F)kYAFsToy zELqt~iJ0`%>mMD%=OoTQ0oy#EjPz=`hmBezxj#Nulm1CJTc|KGTGZAX*?WRq69pO_SMVDF^0ZXk)bjejkN^n|)9$d5(#O z5ZsFS(ly{}jDu>O7s=m8v z+oI$rCS=ju_4X_|PGch@p$sr9=dgb=q?Rxy|LTFLcNLv_6;2<&OE2nakl6eBlb4CP z<+d=$?-bW9iv#1zhcpZ8wV9lr5M z2aL9Wm%rv@RlwTZJd1sZ$#D%NETgFEF%4`pb!0gTm@-Al%0#>++(Npp?Ypen5`qPf z(+X5JYeW)1=2+LcwYQ#s2x!%oRSXjPuxe3|nIQQHUpZhqVl}eu%Ww61&^G z(?|*HRM~Yl0ARY68X5U9ULYm&dgux8AwCQ9)3$LP5bU@|-mHLt zee-AdvAY0*l#}iftt^fXD>h%c~SDXVl6#dtZBAJkqrmH zAUJ@;0yz`BQSL_z@&+o~A2+mB>;ceQ=(+-kK3%Yx9eT)MkCP=iV`tL|+*Ddm^E9PR zLcpB4BGYEM5dqg;GL8)W&Y%4#DDGhA1C#t#PsylA!lNZ-Z7Mh9^1ex8r$b z9y<5&-~TY^nJeSJUUNIemAh_FLR~U1hNAAo3#!fI|M>a8go98@d*wz^e9dCmkkuIa zJ|V3r$ZRV%bTdBRhxBo()8@74i@IIcI=IvfTW{qfQ_EwHZKGyIYn^**A!QHA7q)Y+ zY^^UqV;iHBgJG*F$%LO@lPw2r-si}B5bp=fMSIAdwhf~i@WUeP8sTwB*4pjazLGNd z3SDM<4_#aj5jIHn+H0hpTK_%2>HH(J32wNI-OVJgc0qt~vwp=C>Jfy9G!OqswnMJJ ztHNhsyFNRwTixO?oXWdt;9^tL@^s{$Npp(TeQ&)9WrcEB|Fz(3epxT~uY<3hblXoC zOB!UjcyO%bsnd&ZZ<~v5Uq}b4}8A6`T;j*nfb!V=dS{uces$93Rb8b%w9$+9=SOxHMLid<``G;7pEUo~QwPb<~ zHBM4)3?3In&=e2nmTgUomd40j#cil5!26*BDmYf((bZ0q58i7kG2&@j}iWNPc z9q~B>dKG%zky+j@o%TV05-%3jP*C=A?BUPX-S1cm?O(CtE^?}ZIYdp|FG&fN2hND; z&Q&`WEJOx01A$9LQh}6NGTqCRlc5^e;rDpJK*Aw_Y;*uf*Rs+RU5n?s8I0Ea6SK30 zkhRnKvvF=<@8M&C=`QMN-iIO*?)2b0(4db2an`A>Wr7n#L@i-jR))X zW{BsB^-ch(V2+52fs>&Er2R$STekTZc`mpkc@X&0DalGv_kTjX02=GRg28?>O#dCv z^*8_Z-|}45FZ>4s{3jATlQ4gyhwBk)$Gtdm68Gs*qo`~(vvnjof|58W!!@T*1wXNlhYK4V7K zZYD0LKlg1{`3mAB_M7;=!A~M@Wh?hS=ae1x?fH?YZEzI6H0 z)88G&+L`w;69)+2UFgH1pUG4n0cUstRVmoZS{Bf~U>G*h3Wr%nLlu<@L6 z`24oZAZ2cKK+*X2F+i4wo;S{GoY(lRkq&Tq;4gVjoj7&k#=pD0Bz8Rf4?x3%%q;)Q zo6P^*?+U`+^DX11-E3b0cmQXis67RU0gU#v;Q-}?$uG$OE1BFjzRmR8h-bq82>)YW z;D2=+5aMqgKU&|`M<|I+Sr_^Km)=Vy*M3P0%*X%Q)jtmWw{@O2XxN15y`Po8`S&RQ zxoCg)*A9(o&{$`S5Fq*uA{67fkHXSq$ti}*!Y?`Md@R#(IeL zKLF?sP_}*ql&$pww}H}d&+yrEB%Vu{1F-f(FlFzRRA7%N$ki^Bdrau&=*IvRxzbUe zdlf*^_4PBrUI?7edXPJC(ih0RY_)nOap}<6zQV=!IugsglJ=AY{46OLE+Sa$Rv|bT zmM>;pc^xUtB>?*-_s}z;b(DEcz}>Y!-$@AoeN}cnsg(dkQQhw2P7y*83%(d1kaXP~ zEY0g9PL7frb8XU`aBYs1!8IT!G`O?ee|ezr0W?L%)y0ESV^5d?$O5ao%C&erssfi2 zX^v}`@RPczezq~% z)_$uGuE^_f32|S)?S6>aT$|kkC_mz#hH949k*a#9s2ss-4>f>H>>()OWbqqaklMnc z=61{=mG&7Re=2(7#Jvw6Hj{w0-}|lEaoAkff^3;LF`ab?4J|$}S>;4bDRz>OwATrB zO_6bbH2m4!+@toZG`D`VPDW|;)~SO$R@yD+N;r6|xFi)+A0u_{*LZw~lMSP(Wlo_G z<}eu?j^X-@>XM6jJxFVgIs;V$%5#0C`&0w|y8KTHrS6MphS|88=|9N8FZ(1Ft`vAH z9KuapolhtgnV@^0PH@{Z-u|{Au-z77`;k9%(Vv8D0_aTR81EbXa+U5)f6L(Oobv3v+n*TG+v@Lea}vo4^_k=s48k21|RtJ+Op_K0!? zdk($%H5H&&dRG6rG}Dooy~=IgX`9gS|3oZ7|rrW1eo(p8G_N6!QSU_deN- zr~@ATsljD1YlAWa(Ye*75Li8PD^#V~p9Ii75D^)MCF4tJsus?##CoFZUFm!WIYrOl z;rCFtRC@~=`Rg?84>$Q$&y;L57xH>v2y@GYrV$%zUsZ)M!P>(cT&}4^8`l9o-m zX+E^P^mXLhhbe3b4&YE7QOWnjK**2KNh50l+t)8+dJT+xpw5>iqPeOblTI%Pei`+0 z^aR)@gNVw_uM@4M0F|OC&0ch!8RQ#{dT@m#^y$)Qt8u zn&=sgOB1>;?+lRKAlMg3s0`#bfFt74Ed7tDv|fd=y`lQ_G8Q<>yZeiJ2YnU>Wfv^Z zZg2v0M|aQa2m%lh1`4g5rqDecwU4RbgPh3PhATw?F>NwJXEvEj@1FM_P^H#`DTl6o zti2M+8hNpOePLLz`b`h*lpA54>mcXKK31$u@~IEDzfpgFeQ(yFz@p0NjA|3(5RcV@ z5`5&7{5+Y&a^Y9qjjtbiwmLq-V-hIxoLf3q%r3rf49;qKwVllRz$|>b$tvdY{)n*7 zKgfXJb_f>^J);$Myw1uVBY!g$!G8kDBt`1tfYpI3GkAai>fy%%-kJlI_Wf>VZ(JDX z>kGTQuU?f@CEL^+eS$a%(kbB!S)hg521~bKBWPw<&j#lw)+&lbAslp}Bf?AP7XOk0 zNmB5et|a*ZyqRB?^~x7leH?@`WkRy<8(s;3J07<0Zve{|4+4A4zs(W>U;p1;_}8CH z{qr|b`=kE-eboN-f0E?>CpqW=CbOdpiG1EgKhv*>SG%#YEhbAlc_2=T=ovID&wsF= zzCYC-Q(!-L#z!2;Jz1FEO8I+)xEW0Nz#ANht#R79{so(8Rh?gSus!ceV8 zMfSxysu;56S1zq!y?-t_V0IOq;2e;*!~1Dt9S_C-gty>c;<-yp_j(Xq=BibDUmU%s zmuNND6|I$jy&ia4Oxd2VzOrqnYT@Rp^LW!_5!n(yV=C_zY9o&(l~2hy|0EaA@2%uu zE#%{7dd(591OuCQGWNCwCk5*_Y$vPatnt+xhfU^ocDOka+_tmF>D#T{{F8JH3D+6A z%PsA{kXx9VgcjIW7AeD)n2Hfzxz#BKZjztE5h4xYK}F`t!V*QLP#@6Hs7#Wj9G)6E zgDNahwJt8=RSr$(m2r4O@62Ef?Mzk}B~z8zF3yE4W`wy!cQP0q*hZ?K<}1VY;i@$C zFqU6KEN$3U)XDlVqa? zENaH06a!bPhpK=N%f?U#%lsPYUYeRza`1QkG+&%Y*up$M|FeAydWc%LvsJ11cq}NK zcLaXzXuEIWXd=BgV-CJYw-)mry>Z8Zo~p=S6j;00;A{)yR|UryEbavD6>?I-_gw{) zcu@!a;1I9$^1~|mymJ0d%kgeXh&mavdl#BDv(_POT90R4-IUgw$LK1D?xc`(R+H+l z`#umao7vj#Ezr5JjZk3ZTd(6HQ8-hvBqDWV9_!xg5Ton1_Epyn!zp{;!H-&HcAnXP z)@+^KgH)Zp5nVWy@8wb-lKC?5131!)SsuE3Z+oxVz~(v&r!{I#zyRP-w(=7FPFWm+ z%Pwk-_`ZqMH6Ykn;&+7d7Q;@3Wc7JSKN;D*44WHpLQpe5$TuVk5R#pgpV;?ya+9Sv z<*gi;qXzBw>OyH~m#rSx-l`U`H@yd9S~hgIH+##am0!+jjq%`=Q#ijmR73kv zbhD2*Xgen%rOLAKT1tWD=QCGNgkojI8DSGC-g4HURB`M6w=74kt|Qvk3qEIMZw$#R zc2Cse2afsTfFdp{-JO3#BpaNhhoUSA5l>IaDim!~>H~2*^%ch9^X?lC% z6=%}@L%iyAW1kH={|2s(VeW1TCnJ?=YP;`=%@*N#N45{0;2$|@kT|%9>V4DW=p0%r zpO7o}MJ|nSD+^QW&Tl1G|ID&%{dN^bu4Qr8m{?a52z-G8b!*4WSYSeHJ*9*3kCfm* zG<_?IOn)Dd-}V?_eK%)A$M{t0d#tK_TKt$zs+Cfvi#)z<9$JUO@AJTVS(;|8Rz^^6 zg+t$`-{;CHmJ23mQ;DEWv?&#aJx)`bK2|V_wQlcvg%)j78{c zG?d@24zh5q>Y>bFN%yWGFW%vSodo!kc_)iR$lK1Zik^Q~kSsekwMVz!%;ZoyqM zOc=FA!T=iC#I4sJqMl?R>Y6X^%QJ)-PaUewd!1Fi&nITK1b=d5Kgx8DR?!yek4kIc z;wJ5*1WfrWs&}9vh8C_uE@ z%Yd`VFGL$#MwN8Z+_a;lBY>^KECEwxNf~}ryV5=FW2mj|PgR^WP2b<55~2`tA!K|9_T6fNTJ1@A-efjQ>CX h9|Ppyo!L7imHUMCw)I>XFoR5Yv<Eqq^^72p9J?USYO|b-OVTo zK%x;3J?ZG4HrV}}@faN)=vBji8X4&5I4*+z%?K>;?;icSPe=FAljGvg(dycC%OH~6 zb8yA!FeQv^AM&iqk7l`c?XszutrWu+kE{j@uN2`e^gTZvhZR-4vV&fINV)-RsEsOJKMvBW9w7vVhBXPbNuK%!sFd0grq`!=}i-q zRki0x?K+%geLr+ZO?YzSkbSgVsx><0ouz4LjqmbUwRZZHzx?Yjg^SJ4B-9R$gG^su z91i?iyVoc5I6QTGMl1w!kBQb)CjoxJx7lbDO!40*)*tlvEN=v~P40IhttTX-RSg?O z2{KOz1k04Tj^Ee2t-j7z#mq)i;Lo&2ms8>MdNX91F42A4It;NqLMGH~WPK`YlV1 zSJydQ*G;e|z8sMFQ#ZeY{NWX^lrEQ?)O2fz6VwLBMz~)@9pt<{`=dW~FMi)rH~}lr zL+VP!{ccGp%9)A%a(R7zJEB5YtHpdP50u@_z&9#qxe~xf<#yRJs399|%DdI{O|Ibj zr8dm+Y+z&D8*Sc0Nj^3KF(x(Bql1gO4Li4M^x5Rq>Q_z6$fMLT%WWgfVslroa>3m8 zZXl6N`EdH7b?cX%w~&xUIoJd2hr-b_Z!&eJ|45!*O!Q%*bcmrRL$^-6JxEPZfp0vi zDU>SnVG=dh`K}n+I0aUY_KkKMsT*g|g4BEMxFI(i0&$T3yPvihn9#R5$EL!yA;m*a zYE9;<-PRLT{n8bJNcZzn`~}~Yr-mNntPsDnA=2k2{T|+MFvm=BBZo3Zyenoi4-VH; z5Be&=m6Xwn5DAmH-1mLNOzeqw2NzHQr`^M}P2T5H=F{s{xRm_%4^=}pQcL#5EQ+sI zK=B{BEjr3mENw&cS@niX3~@HE0?JT|Lxq_^zWzN~{oI?AyT06@1893_{rUw-{lZLH zwygQB$d9l9fu6>9+J_yfe6sc1J4N<9W*gZ&*ADvd@y1a_lV%;UDZ^KW!TxWRuN~@3 zb0oj*vzmQ>#0M3!VrXX5BIy|)u{^t5I=W+8o4qK3fo~h9i&UX@e5IZE$%vfk>rIRu zJ{9qgs|59A3TloUb;uPE&r4Oi8fDWic%Y3sq6%x4WUJdY1{V5c(UVD!*&Ius-#zqe zDX>WhxZ*`m9BAuzP@X(j)6D?xeZgrZNXUnh(XXbp#p*nWx5u5&wd*_k*nwty`a??v z+l`r*BfG3AUWkuMVl%`heg0;~?*cp@`y#`PqgN&r;rt21U%zGAw{AabN~kPXoBcd7{g^t8Z>uD_Pqh=psQ`+~JN=-zFp8_h=-kb}|mj=x~ml<~h;RdU6pvu&1 z*<#k^H-~OC5x*`o%7SHfsieh$67Pb3Z8Vb}SRDN@*7fSLezL@;6f^pSh=wdL+``Dh zno08cbJ!h^z1bOi&j935>7?p*eckK~FPGKeS*{+&J(_lL5H+gs>JDh zi*aPOn-N56H>RWxlrtk~r&>$+h-z?@3eDvBsFcEc&zD8X!BqF&R3i`IQow>?M(+j9 z0ypPkhc;2EEDP3oIbE*~cN45N#s^qWSF|uAK@*=)Tivk@Z37q`SV_oi8n-nM%(%y~ zgJf7`<6OME+zms@0pfs5X&)MTi(h;=|>u@@_Cv$b?*^mKoT`F>!d+w8_ZBN^8VfkYqIiDZ< z4=KI-`{fm;-%ylzVXHWwWbDM86M<98&FcGe-SFZ*u)u6PX~3n(QSyxk^|CgBw1+lL zwmFv{CHTAn6SR5Y-*(>EG46GsXU~qan`g+*$F5cIc)qMKSfM7pP36#|)LNe>-AXr1 zXkgG)3+B5E#}lEmGV;m;Z615_@a-p~pm_XVq}%;=R=EeyT}EobeJRO+uQl}_CAH|6 zH=s~yJ{7=m;4LM(n-z((f}LNzdE~i_mz*2x=qD{GkN?s0w4;MmOKDj(U(-LVnyzP71`8h9wnpd%!OWWs~Hb?w1>EbI$BY;`Td0;oInK(2D4x~6P8~N z%8?VJ&u63+`-0Qj@79jbx`z->O(A35W1xh^eHs|T#hG=PGimV&iLv&Lljp)nw&O%f z5?cdl42H%=P%Pos)s=5~+j-9O9xfJ%RdRq zIj-BWv2Rn#n_;0EYx8JCTAm>KX}CALmbt4CSrB=ht*p{zof`PZseudZPXhWyuDX`#WlUmUF*ay2GO%x|qQO7miVAGM%QPI2P# z{Fp{+2w!`<^thbVZf@;m&M|f)r(wt4&W4qX(s5~hgD=f^K{+J{m6HGfu=lIyBcqnfNB`SV6I(=68ys|@!p#s&_GJN zFAb8^CvA8)o8Gd5S^01_Hs?Dq`V z-MIF6KkZ5Evuc#*%Ixn8K5-t%wEbfs(@V~*W;ichkByUvR{?H$fz>L~zI^BXTqb)r zMYYFFD908alkeSqkaLb%WagcIrW{yss?PeBd;$WagCatmvrZJik|MU-MXD zoMA2TaRUzL)K98~xk6el!hRqlqhSF>xdP9-qHftN(34_cq{7-}2QI8EQ2A81)V`8?p|U8BJQDRZiLWVPyN zlHgL1r^Xpx%rT_=hui?)dFIP5Q%1fw4F;p6PH#Octt(oknPDUOIYIdcpdTLSl}?z zflGpGUvAcT3+?5fs1~uDP)8@dR0j<;7RgEo(r%g z`@kF($vaZ)of-bxCvrsR3C?NeKd9%RjC7w#sLTe1{@z=4(FasbAX(w*Ogp%P?|F8m zlQnvvq$Q>@l4+hIHz7k^xarXH?jme+S>^Y@)A``c6i$^#pEzY*{`6m4t#+cm!PJ5H zr~9h;Sh(y!8{GDZ(jI4>Jx9?{$~~uz*N?LgUxjrV>B~yztaUYYm8qA>X1mAxb-uZl zA6%55`vaw%;>0_fO-Bb<{I`p95?*sI{foG|I-*EQeP=mjTxAoBQsBNh-c+|=CTZto85Ei-Lj#9^@`Uc-+4m@cHJ@Jrh5N6}140foNlkh8u zT&x&gXnmBdQ(nXst>OsU;JMh!-`H|jvNDrl-Gj~P(@51ZI&pa>(H_zx!O;V+hRN)b z`UG2-_^Ph!%VruZgB&-FdC}PCm?NSBPRG6&%Q1OzW*<@3ORa z3RRFBAbmo^RjH-loqlq?W0=%bWweuR^h(Zyn$c5XlexN?FVBo|D7TE=h?M6x)zPIHVHp%8q zFxZ^w;b&YVcud{=>T;Vm;cuzkn8`uN5Ad^F93Pkud!}=Gt}*q}?k0sr=Nit=1ON*DkkHXmcTT}?;Xtff1^l0(L#SfW~FYtKS?EIC>t_a+9A3! zUS=D6GOBn{A!OrEU082{ZVOH8`&{}>qxF)UCE2JCe9;kEsjA)$qVB}%U<*Q=-}e^^ zm0QI(>K^mYK(;o?wNVF{x$3&bz#;f0oHETaf*?d{0p@a^6(e<$*Wf)ik!vQUy+ z6qScgI!H)=^hJ3<1#Rk(6mM>~X7{O@X-U&VHU~ z%x=llA33f`|1Bajr_+IDP!i9j>b!O)wRm1q5b?D0nsD&?O9=e%4ntINt7WNYmCOm8 z$UL(~YacSA0!-@zY<4+KD@ePKJAM<$uzeGLT-aFnntm9~si3kEa88#zoIbsE2pybA zQtM=7*w`7FN@KJwEJaZRQiRPBsMnPYzG`O~m$5L3uu$rCqwjg>6)dM}H{~JqH~qp2v9m73R_oMk?>b%Rr^X zt-;&0!&B4zhlPcC-`lJ@j78YvL>(TemtcjkO(k3UQ35*Hi%J)LDShL@fD6gE z{h(uwbrfM-m^Xu)Sai=zT(@6P7a$8_=4u|eMIg|fgPsYTzxyd>uw5rU0&2w z8?;FE^GIZTVhZ7UeN4!6_L1NI?fH}wVMy21iGzIQ(mF?ZQY0; zehC68=Ya{=nT@j6_T`+cguYd%JXp6d*W$jt1 zkc}qV`0T5w3jM9!Lk4A)caVJ19{?JWq;0E23jPVngOM5Up!97Br0Ouqnjv>H<`)nF|f;BQ1%UswqVZbI^B&8xMA`-&(ny30UKgg zJ#pTe7#&q~+b}$(uY4k3te%%>qD(nQjIO=%Yz|@!*xGD4F-VRe!H20U_;MPS)9;Mi ztWdR&$Sf#+ftsHg*`Z-7!Wp;K#f^6Vg*y(Os8KM*|2qR6 zsFmf^k>mddB>f+**@z_R%e5~%`(B@qrL+=$w+`WxbUqziF9TVNhkuIq2=Pdz1UoGf zzrOKN8R^WiYSsKS?UJAvdG*iv1nH|*<|(OLSSoMFN+p|&iUbfXecGztDwRXfEY%7$ zAG`u0@0|WT57j!*_Tb4&h^0GxKC}D?HcP&MMWkpjF}84jae7l{3vpVy+dA7HfuFpD z-1!tK-}YTzQVxG9m!@;&l^D*-MQt?BdC^)f$7TCxfX zR6}GLK{FbqH(r$5c}CERa_L`pX@rTy6IQ(u`ZEwuE<(BG=cQ9sD!?>vfy90A3rYH3 z26?`Z4wu++*J$VoVetE(i8lvoJevCk;g=u4sM*>2y-eLu6R0UBC)iCOXPDtL!+K|2 zBr&*K`;G&|qJB{CVfCYLYlc_m+{GPvoHsj*9Acby$J5$|SgS8kY}d1sRRdS+8pJFe zav;6J(~6T#m$H%?C=(?F6($#Zll{q}M1mOK-sTG9@47EgK6vBz@}u>4rtPmEfD(~i zflT%8CNeQmIjf@VcuO}pWRf+pTag`>a;@e%c10*$U9($tX-ns7Oc3KPKBif-)cy1u zwpJiCUaHX2z8E&pEiNrkdA*)8@QpbTBFTk#g%(PdhtC$nww?`7?k10K=Zivph2>fS z*+sf}+U=wW^YulVt3F@j0^+aZ%z9zdZofT=Oet~K9gcS>h8 zA{j&*%%yT+Zm)+G+LR1syWX-cTug4&S&w!7EvSCIkFJR;!BoFmksp6{w$p%CUJG#!ulvx0QBtYL0F&V_+4hrOTO;jIr; z;FPr}^O8rtE-HcRl5>e;buw3Om9DcWM)A#)je1}yoll;lMFdPfWiJFX3l}b~DW#;^ zbs}WCyFzmsa(p3NSBzVSYQsZ_>Q&d1GeI>`Gn18?@KFJ07cTJ{1DsQd#O)Y_#rp>O znZO4se{z>a)SH{xH~<>y!j|Rauo>mFqTcW*Cu3(Bucu$WoZV-Xd+!B-6~MCf-vRL- zM$TLilrwKyjt;y$s5?@o!50|Q{_y;8!10X>DbPAuzc5f?&WE*FG7qyY{cXVO-bH6v ziTgcHns-@XPj)s_`3;QAd+*h;G@!mhr9$HS-kk;lDV(i*JV)%i#$?MQ&9Wv6dNmb?&e0I0Z zx!DzM_eIt>y5`cnd6y9E;yhv6aVvH^@1dR7;XbE2+=0Ze<16WI>!!6wwBY{XV0=x} za~rTtfc-|) z{OfAelzgZ(L0foGRvw&J8F8<1?UEa@@11KI5Gy5zfkaHI4~jJJfgR7s$+e9de4Hth z6Kh1@Xy0Mr;g6A(UwQn+{z|g@>9=gAn0n8NbL%tgWt0*XPTA}3p!jx~d5EdEAgWql zhH0HFT$)W1nQtnv)%lI*1fb}WvXxb~BE^G30fI)C((?4ngD4Bk zaIfAbDtJ6Er_7jHFZ}hwh_6HL?tNURv2NCM?q>r#QZs#Zjat}HzfoaIPuXDGU9hM{ zHaw{~=zbPy=(jWggyzJ`+^L!nmp+^CUUssY{0I~;oXUEnj2TIxJ&z@2lbM)$ZR;3A4=*T6+fkTPwSO=S0 z`#|_Y#Kw<<%&e$sQ&0Lr{upqg4nf4o&qw!RVpPL?A^=BT2Sm*r)~_&UbjH`S_j@_i zzek3~b44H(=g$e(wVUOGCwXvMnR#qCYYt=Ro8O@5V@>7|q54vF&iw%3ejU7nTF}RX z&=t2}T@D%{7U%e3{E)ED0U*}7qbPwiqvwV*9jVYT>2CBg&Z9U_E@|tO_OT+tvk1hf zwaOCJAnBQF3%Oer$t84k;6_6?X~0$eI%>iR{dAxL)$#!RAzZejPK~Md^{XyS_K^y_7&u z#ymxME9s&P=3h_*{~>_0T{%`VQcb;QlH(L?DZt+qMPbs5+#Jo}>L!+ZuoI9%!hGkp zQllF3C|?dhrzqxaXa#dN#|J8{Gmf8S)D;dNwf2T!3MYlX&woEMfdx-lXt?M0>efS; z$S!TLlbh{Y$YfmJk>=fIv2kl$Q)!wTi{epCkqQI&Hs9nig;7@k0pOkEyMW)@lRjoG zo>DfC4P%t4{~~P8{c3dkBW7^URZ(kDw#n)muUW|05ryS*r;{Y?Pb|CNeh zKBO_%#ryLy0I<99wZ&KpOwX)2u!Jx%pOkeGtr2)Kd#c07_Ge!Mht*I8GgtUE#tHu~ zlH=?EXf)&GX}A6)9RH16LBI+@q0(3!s#lu$c~9Z3Jy9cBTO@X;^PHxvt??9QLUpt5 z>%2jih>J=&?amEKxhq$snzA9QuAqw^kguxuSi$Yqll`~S9r%f+pZv!&$Qr{iHW_rr zcRqkJJd>ygZ&C5FkI8nfKOC8@|7>_5Q~K&&UeEm#)Axd%7|aH58Y-H6sv0m%?(Wp? z2{Kq7t~QC}>MiR2T=}i#OJ8P^WlxZc;1ij1FSCyssK~euU+((zIk}0EIP}3ow5E8c zYwup={fZ)i^=kFDXn}h0KqX^`DjgG=i`%NyKP7`J$xoD@?e;`fx3p?3^0csrD8_hJ zN;}B?WTIF*4^fHf1G+hIsd%HmOBY)P>2CFm6qAi0qOf~`l zmHZaaEXV+#-B4&-cB9YAEw9U&%rh(Qax`ClWGj!7u=1eyIyn=BFrk{4MqO5;ogvD0 zk}^8=BM2h>JB@~s`Nm`&Aj=Y$ryUk6i~(`u#9v$<4l)R7ov5MRS{k;S&r3nd z@kR4itIj5n)z+o*(t=+>xSc!=$T@G_JPl@jJA4j`ji&`;75a~mG0{!Go{P|VF00bN z&$T@47T;Sp7?)}DeEH{umQyz!7sS)g@CLZNw?k#V`!y-@{?tV`U|MY_Ab@2=s*1W< zsqYtNw19_`>=Tp9tD|oLI6P?9!BfqLszN%@b%84CJw15q*kZZFIgY3VGGVbAz!E!&OCkn#gi72cqJ1v zy8FE~oJg;&uOV9*hqpwSOVtU+y9bfvSQ5cPb*mywA=@jevV5Bh&MndVok2frR_+tk zsFIbZXGIgSK>yCcw8o+9!MBxqXWr2AZ1ZXt|8Qx!DbAz#MO@w)n#X0eU(HNmeWWf~ zS(4d`1PYQ`{z|zvpe?JnLqjCSF80JD__>9O#CM18YM0rKXEVm~HAx-W^M{^oog$VX zzYUUvl<%5Pjm^Xd)~voQ$|&r*ev+G0cap1%W$S^qu7YkZk2wQzMzW6*cx8@5DjP5DtUu$;YH}hBNR9{VFqDxJIp|JLL!<6t z-H8(p)6i9AZp$}={@xc7jxacxjg?Mw)^j)Ng3qd{c-J~hl~xaTZ@tOw(ZLa9$j>Sxn*jgXDg(F4W$ z#Vhr+XFG#pp$-7L$tYb3w0I#~m=hC#$WWY*g%c`jUFi`$UZx>7N%0X?tYeg|cephu zEsv>D-XpYnU?u^`zHagATcFO(DH*_qfT6`9z~nTuj$SyoC~t9$Ky zR1$IVloZ()xuQKd284LZ5S4A!P;sCq>zzoy(cB8(o-Hvql)5Gpcmi#X5sgy;dLXjB za|n0<2h&|i37$%MYU@G!c+^jk4wj0bzg2o%Z8mLsQInWO-67rSFY4A9dp(%;#*oEO zU&Vm9_suU6$O0M6>2?c*`!o#Ki*K0%3i7TF;sUZs zH}X(DHAQ+nO2`|j`elbJySqfEz85#P7_9F**#1;K^@0o?>8e~Z5Ox&WRJH8QM~B|P z>@_fiz)~ftMv7KM~OOF&0Pjrv;$j$Q=%f%A?Pw}EQ5sQh1 z+!o;Ig@b~_d{kdns#5U8Q3II?RiN<4mn<`jXy^*C-q? zCEirK=@$Qa_`Z{tWQFbCo+){~`#KWn41BvtPF?t=3fd*?j)E{~9>>^=)l?+Vh4?sk z)F8Zdz0kH8;_CL5eFeT~N9-H3Oq}SscZScAV0tWlQ;tniQgOUsvcSrnKrDTXq&&?) zDgk_V0LuPZGJ46#sLX{P0B$GUE0_Z1baA~2`*7u6D{pZWPrFp~&5Z<^+_d47lGiZ) z^CDV{)sFMaf9+nC^?jSV3^o^(!!3O_m@93gjK6zCw5xhPoT;03Z*j+97AL+g7v=t&w#tdKL>3jSbp+2D}ZD?+nzK z4!&8)%Y{$#h=%OH)HXc!etX|iw5KF*G%p3> z2_Tch9NGowPIZq@;H0zNfOl(s*zvUC*h9})JjqSgGI7U80fG=^XiqtI0`%ynbyW7) zC9z{+`R%MJPU8GY6^>tw1X!faU9Wq8akadDlb#99$o&tv)-SvZ@FxEb`1;!m|Gu^L zSIg^vYHe+eKKVZ{@Ly3t>HxX~%Y51-rR=`^Cgm|)tQCn?BV@QwVXwg>tO5Iy-{LV= zoY%+B)YgMpwmrL1Xcp$o_ao*xwCYx2H>n| zq*?VNSImv8K81VaCef0gAlM63k3g!{v~s;diT2VFswR_Oeg5||A>Q`!!_7co#CVGF z)E~bl%zU5rp6);0etP@)?dQYS;6P$}M&RnxUsqALFGo>`@B|}%y?7x-2F6ps4D?KA zN-gfsT^CtdA5wU5|94;?4?S;PQoE$~i&72X^4MP#o$qwJ_%eExcb`Ko?Tr2 zYvua{Szn7PKg{2E`MI_9{JH;jYyUv-vGCviwoA5M%M73Tv%jeOOnQ3z_|bAeC~&m$ z|Kj@d|8P6Ro9T>~?fs?UipGh~s#~<|zvTK97V$UQ(*R-r|Jgl2&S!~lWv2$SpY~wZ zkbhHE);Ljf3kc&WYoWg@+&3*dgMZS>*O7~ICOgM(6TZT9|B}6$+OfZ>1USE4!*?H? zzG_2Ab5D1B^_R;1>xwjR`2W8btN-_k^glpYpYy6tW@KWc%X}Fv;;)5+oLN@(SQzwgDX`j%)UEf$ z4WDPTiFClDsM(sCapXu^qldOvhGk}|F9JJq)v(pP=mn3p0t$T*5tVSFh0dEV-rQH4S3 zZMra_MUJZ#fIm78%Hh|ep41)^Q6LiYN`iq>0G<^65^)4AD&q$(e5jmA_%Mw>s>oTw z_vSPM8`;0Jx5QD#F{y4zZf^fT10XIbBmQoIGRnEY6rADOpEpChG7KqvZmGjK2iv@r zw?2(4`)EsyS&5HtAN))%(EXYJzi3$QJZdnQ?Y0t#pO130R|M>m&EF z{AaSd>WC;IYT$U_2xQ>ct?^0?CZ6U<0|0_B{Ur)@CMeJ%w3#iKPr0bejgb~Xs=`_# zcZJdT2Jm{W`v%I(d29Lx?`;W*W-8UKMPTypIu%5x*YY(E6+H4Hp?wv<*{T*lVrU>L}Z_M z=*cz!Br~>>%0PK3^U6R#2AXp?GPF5vh75(?61x5wKSxeUbHUJ6y+E+Arb}CO|Vle_kVZ-5rPUW_b2|#8yiIr?-8m zdp0bY*noRg6$)quOIW|z6Rz|i;4769$D<kCOmia!D+B-;VQAx#ekH3X?lsE$26WWTc##79w1uif% z{=iH08NyXqi za`IW!MX4U3!-A*GzRm3?b^W9$K4n)}`}4go!=<>IO$~I0k8<-UCOG_a7^9=7T}iTQ z^(0OLsMFFyso4VsV?dAI)WI<(^qwMOtXOVoVw}Og!$jB`0yJdtR>ed>$tEH;fBZ*qEF01^Vs7|wALfS!<_G7$@_`Ac;-_Y4VSKy@*4-la%0eauwJ zE4LA(_m@h|Y#^CA&LWufi?enZ?$$FrSfZ#+aT1L|ymIX#N?Si^V&VK#)a@fO&j3)z zu;h^%@4Y_yLh3&Sih#!dk1qgm+TYt9|HbuxIxzo5ocmvep>%YyVKvPyRzlUzjHj0q z_x6;?od_bism(sd?6=sRY-IawegVfEGFw8{v&~tx+>)@zy3D zhX0Oe?R<%6_bNS{LOPl%R~>$K^q%`cWpwbWh6ADciQF||3wVok$m7@@@9}VQZ_8V} zB@Ua|p9B3w^B=E-6H@xqJ_LG~9(KeplgI97>)S$jL%wsahaRkiCWP)ygd%1~ePkA8 zzc&}IuiM`AunEf3SX#NF0ZW)A!lhJcJGPx;RY)%{o4=Op6E^c2P5jEOj7?yhOp>w{ zQ*>Ow*w`i3mbcgSf*pZE5wuDgE0$eU+Op|`t8iM{_IK)ee}k+Vy6L=*oK&%^Sm}%| z+`@$(WXY&(Z7Hps!4jEy6h)k8;Rky>KX>$HGIV@*XaIs4lXkCHL_3c!V0MPgE{LhJ zT2?@u(-A8C-JRy1eOB_JLKS-pu}V&>Z=}fI+ZLT>m$k`ni`F_vnP;_`nK6MY*Q9)h zBx?4n%H*c|qIhbYu(Y+sX>wD5<^SsW2~Uq$+DGF z-53SI!rs$Dg(E5h4?m1xW(^S>V|hkZQkA}oQ4?~dDVbVX`9~ zZaM$vi#L4=`~0LENOWmq!xfpIxxN!Bsc&Fq!zn~JxsV;gmNC(CU=jLZUcpl%sUZ)WX?ZvvLM(hde<(yq zDw{`50bhW;yy;1LGh$q-pT*3B>#l2y5M6;(yv%hVkCKeP&W@5)IAv;#E%slEja0}N zHMou+9^f6whx^mrIvpmkVL`nk1$rv25LEe<>+5#gtErmq$x7!V}JB2Do9?%}B~ZEk;9q$i{mpT0w#Fisq$Bl_3?Ri;fZh-cLv2#O!Qk~KPdv7UAZ zLTe$*$m4K@A|u((ZM$s~d$}1+B)UJsS2_ovL#Y1IN=*p@tomW3Yu5 zI&pGR@45w+9Mg>mr8S%&BV}IAKYHG9>{fMdfZ_EF!B$`H74GP|2=^a*THZ5#JVegB z^9wE+*y^*>EW`DV_6AIPeCPAM+q{d=B~JXAMv0@}2W)a=O)NN{rMWQ1#nb+4kE>?B z{7w%yAjb0#a?ViS8`CUHqtVGXvb5FO4lsQ6(m0U|Ho~IReJjx$`RUaIc8`cr-cDO7 z$6qdd0XzYR2cjYF$l7mA3W`k>!a=5Ho<~_LU0VCrRU8$fr@D=P&zf>U>e~&#_6{D$ z_5Rp=dGy8c1A3+5?{MRoH(w$?VzD)j!x0Qeu7J);q}p_bdD)^DCy6%#a=fRgc0sUA z1mKX2Z+R&MnfiYSUDm7me(lJTKYPH^Xw}7PI+P?_bzwz5^QW5~bRB(~f6Ka8s`vbE<#{3JX>p_DPyYcTwedz!;uc z1fPx6n>BvmNl0-}Zti_;g$-a!iFtS&YRHzVRU$V_Gl6`CqBFosv zU@#)<7}6MH8-5?UpYQYhzR&%=zt11PKY#yxUNdJtbIx_HbDeWt*ZX>(a09(t?5w=3 zEG#VSx3#X{V`14-!NRgv?H~wf>1Y$Au&`V`di%PD(c>X%a#%U!MoL-TwoQNjZB^MC zY)(#&1J;DGJWZ7C?VWma?%=tDdoy0^nnY*4xp8ZJD@a*7`Kx$X~a6i=V zL#_uez!%i~7o!)H87b2il>6>JRMY8l%19LoLD*hXKX~@}(QE(z@&8r_ko2GmC>yYn z=T4nwVR`8OKRsDkp5UzA|L(bmg#{Fb`|Ia{oj?BdPrv^Hym0C?==akEUlBCHa=Ejjkm$<@cVU|f*`TK0C`P<58F=ZxtvcAZoJ-Lvz!hhPc#kG z9xq0m0kNOvJ;nJ$@}WDV@qK7pzV(c~|MGWX9b(6bZk^h`_FIBpQg!Q-b%T=;ymB2? zEwj`fo}UR2%6N~xULw`ST~x~`ZF_ogu^1+WOljzA>BYNaEtv)s%S~@QrP#C&bsS-y()1LWe4We%&rM-KH+`Kwn+X&T~jrB-MFW z%+*?J-Za7v>hVjKX6G!YSkXHuQCJ%a#`J7*{D=0jBx%%;2;X!UgR-@vRO7dN+qr%a zY^*R#_DNG+>`3(qFHr+=ySkIg=XGXrm!*YJqQMR0^5l(uY!W#o&VxfB*XET}AqxjO z!{rg-*;;_?U`bH1YSb-(fXf#{#D46}M9uV1qAO6cY?wJViPW{%fQ{a8aTktCc{O5t z@j#T2#iHJt1+7_q*nwzP{m?0E`4^?!m2jiY#B8walS2v$sv?_jRn_eiTvbL@pppVz zn<9B(q!)eCAANgIPNg3ZkY=A#lxql!lvBhEzj66UT%Ti0hp*BLw??+ar#eyu9o-%D zk|PQz#NJ=`2JK`YtvZr~E>7e#bu4-(UrSOG3w&w{vGkIS$Lrw^^B#SOtU7h(Dy_>V z%df@**43uQzPj|))2BN+kT93%lPj({0Vc$EiFwN&rk2JiBEUZ(6 z4IHmz>Gy6=EdDZ89z?^IQU+1fG^JL#BR$HsnA^==wVUJX#rn0MX12p;H4kuW*b6Zx|Pam zVFI;;+jqzwSP{tYlhqG>w?gv?rpcNqDa)Q1sanamz1$WT1ncEh3>aJq=-UpaQOHAO ztmPK3?F+xM@^jtu^It|h=_X%;z^tL}Ev&~&RaM=$8;an>?d?hKZC=v$X60cUirQTd zO&4T8oI-(l*B??bMiD6nFk?tF);p$9f@7%6m%L?`vqF1Xm-vgkSRLAKNXgZ`;j|u@ z`6E$)by;enXZu$jN5b|he(@2%>2fyz#jPHEoKa*U*|aS-W#Ie(#P@YF*S5agspQu^ z=F@Mz*d*?&O@V_m8(kJt0--~%CFNeVO&p$r2Fp6iSaVa|BumUm9j*?A9@Qv8|IeR-%`dzPLgJh+1Tv&j5%_9wn+ok>;vOhIj-aSa9g9uCP4O~mibVJAYNZp zUp6m8r+MsiY@PKIDsbkz@vL9(c3zIBVpYCV^i-^XL~cgo7%Bo+`B1k!K?CN!kY}bJ z7(V-YN;2G=bxGlK)iYg4M3Cz0P&M5xzgHW>rU#M2JdAa^u%MqT9g||ZKRz6n)qkFF_Vl#X_Rn95Snz0W zTHA*mAv+$@M^ecOVsV8XuW^+*{t(z2|1DVDT^rHlfZNlne8iF0e5K#9c43UT#+8wU znyC5hn*G??V||~Yi9R1U`IB@cEJE7sw{FT`n0(rQmqa^EU*MTpEh6`{{1Jj41rc)? zH4mu+--}DiOunP2+#O~~UdbF}7>vjx<7oQhnQrK(9*D?v>sX&-p`FYtSNb_^;@Bp} zB-!#fI+7JPmQd}rSZ&LSlF@EdRhrq9l~=0Ukqfl|;roC}8tJmNxfxkZ$vYo|c5)BJ zf^}yL^vD=r615tX69z^`2!?T-zD?Ry9{X+IanPNky1eqbL&cmss?+Bi{@JZy3gL?B zSP5RE?L+vpQ@HFi=H?BwK+~g_LpoGbL5$&I?|tk_R-kF|gCSk}u1l~4al@iT^l#Xlm^fK4o(f|skLcJJ@nDpeXyNu#OAb+zg86J@8A2#{gipByi$$t zB~!>iSOI!3L};9CGN)9^9MZ6zRUN0peXY1aarn&$ zXDvT%Z>axxjgQ)wF{z3LZm{Zq-Y|#-&`(aCujI zWdiHs^22ib^|QF|E3kKz9=?buX(`85dPkRbf4l}>d%v(mAdu;=j4ltET9)L$-_wyI zO>BC*wdmJxoQ1QK4bJ3!Pxf{!1w(Hq20z?XP}5XaL&gcr(gN6}=o0 z>0=INbmx%6KFjDb@Nd*aB`<_*AU18QbW)X9lS7)08)1iz(yZNiDf(^8*|UtN*me;? zU_soLagRSEg{r0BLqpnrth#-2^8RQL`?1^sHaK`#L63`rH<+HqooH)!A=-#tB5{rH z>sz^(&wgE-Iqyk4d#gs2uCU|a2t#8h#ug@~?sDmShKJ_ywp*B)J^G%zw?511T<^EYfCe6k~r_3bAG0@3(NW4Goq&TpI#%{NjXq{48kO5#mYOY?!@!}V$F z_^nNHaLa4lY_**HJM@C~8*ciUKHtLol?uY3xRxbCcZ)^z=on}3h?a7TAJI4zm*pSG ztEZn@djTVOqEN=bglmC$`dV_wdnjj#I^a$`6`pdn88_XGdh45;^8uSf(KKXecueB;h(0Z9?d5mE;8WS{JX1eASp3o6X1kGThA z@`;AyANKhMavi^(F-OQ!e3=%mPguGRQR%ome4ur`wrSCZa7|h%{jQ8TAy!jx+xg|W zI+VspdxAT`zrd|-P(dj@^2h=dAA!kOj2+ezyzAd<*E?pOjyh&sy_N>=ZFpxzFjim( z_8$d2Y*s}ECvbKK+EPs%O=59bet}>Z{U9MEyLR#Q@j}#ocOlWgiaBEV#J=*8PH3oRC@)j#Y;+&n^aS2ii;rWrCqnBbGDF9CKvXaflpMA22MXF-npib|6vFBPsOu35xdbRKR5;$8w zYRi7G(Knn9Bo+LHiTEAJpR+6~Y z=N`HIZO=O-qf#7nq-3O;fD%{S59C2#9o^fw3rh^$W}pt;n|6rj?&67Z)^|cR=6{aZ zlQH|U8754f5I@~{g;B)|5cwcQCoEFN=>`9O)o)`8umSt1;`I%0`<7>uv1|(i-#HOh zz1i)br)Nf6z1n4kI}_H5x%cZ?^b~08%lh3wt&&2{)wr4w<* zrr7H{E17~dM|>5~myLoO?&fk|y_Zi7IDDF2b}}JTPqyE8eIVz;wP8j`@=k?5BySDN z)Ho3en2%f~#C$Fz^$LXN2AuWEF{3|{a856*d4`4{e2->pu#8gsnGV@`yz9!cZ@*II zSr!(+bLD$HKJY8~ibsOe2BJy}l!K}l7Vs5vt2e;^u{9H-pO{QSH1lf*yHxsQyTkaB zhB~|?UQdaawuO$0n3X;5Wn5u8j{M>;hSK!h4(=LiaEL#nz!6mL{Plrhp!)n?E#jz0Krc+|LWoL9VWYA2O=L3Pu0(s;^|CXlk}2Cx zyc_T=I|P#AIv(lmxLxBdnN;<|#r?p_X;g{itrH=9eZlcaDK{C#eLLy0`4q^9(a(Xf z@HVLQEf^lIc;Hq$zZ~nkoxBnonWc#SP*_sNf42k!exBR#QdTLeZtxd>Z>b?il_1rB zbUxI6$VXO_U2oQzVJNBX2HKfQ#T$0LBsELtp^_l7u_P;$BF_txyi=1^)0c*%=B)*U zo~2Wmy5^F10#9~fpa)2p9BNM0&#Nm_Fh;q5a#w!8a(NlTN&WZYJ z>%&P(o~_x?!HN-y#ZBLG-eae&iSQp?(ut!L*0eW_*l+2~i&NpZQi@1QpcVe(+UoFM z`4;^d_t7Pof*)gdi($k)hCxh3D`{b{vQuDVfd6tO}nZnBPNgA!%85{W?)` zN$m+ZClzQR(?~d^3IIM-%XA|5vay7;-HZLZd-w?+<7 z%ZeS^6`Swqty;DMq8Z zDAIPnE(u>gkrS`XHSIptS9u4-9i#Y&&Y-lT@#IgGC>~{;EBSM8lA1@8fXj5*$Wsva z*ig2NOj=i|UV;=MKYtZ!t~P7M zzp?fz>@uqq3!8+P>*y!K$;+_1ZUwhWlrZ7Q$7>^8<8+FgOq8mTm~m-8+COz*J@eL^ zC^Uj|R52}WIg!|kPZTFtGET0!?%RM-b0}ZgOhP~Vs6dtqEK1XRO4;Vr+p{T2be}ep z2c){r)XInCR)N5uPF0uZ{Wf*%4Yt!grhoQLbPgi0BEA!j{i~%FfnLKwCGS~m%%Kowej|H!HeM1S~FrB^pfZY(1%_U39B?#@AF=8(mH z-MG%GFyn@r&3lJk!2Uk`Qc>!gZ^Yjk9TiaZ8ou)Di($4kGfubVX{Lh3&5N9GE1ze< zOsfVL>hwGJtUNwEEnZ}wR(`SO`;4idY@QIm2#TDf}rTTNYJnT&8$vI0ev70P68sZcc;v8MDjToskNu2Qz3FiC`k@|ueh z3l;T~Gs}o1!x*v<630Fx`)#YvWq;YDep>si`@8CBGq$Ew^fQ~A^MTiDsrkKJMn)}(Tkb@8bh1;Gb~o~ozlC&$5GuBi@jC_Srp?ddN#*e@ZobMpk z>DEWuL2ZU?Mf$#5jgTzV`f4Tz_}&vBkQ$_<6qFr@=CG#wH<7+ zbbX;m1u=6%>`X%sghc`fRu;6UWdnZB_>W9)u$AdE*0;I&>)t@o5 z)gk&5AfC@gKfP)>YIM6%f>h=!9D0%k6vub-2tTLTfUT(Ch6lgevJkiNOEj-GOxJJY z9$y^zqKIy2!H`vb525#NaCzZlKa-6Q6kJ|Z8o@1VMVvXpY_qhlA85(;Zu4>dE|PhB z<)$+!{Rr753}upYH{T@)49bZrOhwb5G1UzC75bDLk@qm{5(J7#Ra!q42wzBtgf1_J z_}#^#bOMKtA_S~wN6R&twU`KfLYmCAQo&l|B~VVnA9a+yz`-GI)D^)m-)%gZ*LBxA zcTUyYKk5N{PRHRwxjNye(wMxg~}*@}|T98sbdTSwrzv~nJwBhJ%R0pFcFb|t&T7Z-(jAai*!nrArnwF$NS z?St&mGVAGMF$*gNGtk^?Uq^zaT7g_-oVCZxjVH1Fu&G1tL7s&!rkNp`X14f@Z^~^1K2!;vbLB zzWK6u7lrpdpPKn`7p%Y9(RC^bg@E`AtnGP%ThiPM1aAKpUhlS;Tk|E|d`^6OpZ8q` zmbuEC(YB~L<(j@&E)==Z5&7#qK8jjg)9D(>cVBBd8XwT=opfC}@+*0qGl+nV)| z$u-|#v&Ev#NVD9fa3Bj!4;V_1u4x%2vp&*&Hk5NKSS4A{dt*udOX1CfEw+N|X;pG9 z`2*`+W@N8f62_HkC0Oo&?KDsGU68@Ixo#a9Y|n9Rye-uHuF~m<#)t#&ZLJ3MzYpTL zDiUEhv(^8lnK$k;c58J|?&e$-oDIZud#E@6gs3{5`RXI7IpYbe)HoES@j zP@PhFTQ9Bb`B{6s*kj}*nby-~N+G&_=8Ts}D*xDXaGh7@elt?R&e*j_r;bl<(C->C zgXuC_BAe4OBD?xkj9Ma|U}rv^sPr*Lwu0ZxXE1D!ZaZpw143J$3?Xk>?8yi>h{YgT zkKfwxaWrplBwvO1`bK^C!K3VZmgGh!HwRz0uZXK?((X@>y`d-SykPI9X%=sDhehAzv-&WdS)k$_OqGV#sLsdUO6;m8r?`OzZ9 zmGU45#VyopGyXRG(lisA$!l&}E8G_5Wz!#(R^0?>=W0mrSOxDM%gkw)L5WWG^G2~f zpT8y+)p4bi{3jT{jw$*V+oz3)ofUyav1*L+xg9q$cbCnwG+#U!m=r8E{9)nW##!l+nmeKb0BJfW=+vGKUtxbCjr!sVVE}zoJw!e z5&t&!?3b#DU{AzgBl+^>LTM#7iIP|MilzN;6*Dp(em3mhT(!>;e&Ay`d&%g0J>^hm zL7{LHw$!Rb@xCZ1XV#IKzL#l5i)X_9lTfuNIfLo=qiDUFFjlx2*{L2%#BYZEAFR7iX4# zv6rG_mmkjyw}mmU&k z%E-t&)%lctXFN<7H&QMZSJ3fotbSY;ShVo_b@2zmAfP5`=jtw-(^*Oo^Aj~Zo0g|v z5MpAH>hm5?E zPtvD>pRyI7b_}spYz}P1%-(Cl=twNAON4}IPbS1zg{*TFPiGtol4r(_>Jb!Bfg*av zPVN%q%paOy=|pEs8z8}K0_9XT*}7ui^?ewpN@XODjjy#8*6U-$1o&E)9+?{C^GQtwHofUI zedRvfjp$zNqy}-`=S^C`%9P!Jd}wm;fX8P$ck80>+``txlPAPbCygE;9kx{Qt0}rW zVjE4OQR?!16+?S#lsWj7GS2C#-5CNQ^Kds>!n__S6tODIp8|@xaYeZBNPh6(LqnL- zBIk#_mXLEYvZYl4gR^L*eZ6n+HaGQNavh(G8FO%-D>=srfytU&bvA?8^pf~VJW0INQhoUp zMOcpvM$yDn?ZSdOe~2L|l*xX4sQA4F{Ih6lT0mr5+Y^^po?9)EnM`N1y8GXHTXNFK zSMKi-T=C!k^%XYXg)cb{)!tY8gIOzXxU+=M1*-dAOa{K}uH+vLzTg1jz6k7>!A6(Y zp1V^!ML!dxd{LXo>GxUm0GD%6=>5+MM@pA2x}dvzy<$*_Y4nm{6Vr>l$RUaP7m@eO zK6c0w0$zUJ)_Iwrc=wm`oO~AibSZq+TKK*|cA|csd2pYL_DM0BC#Y2+v1Aw29T|L> z2+;)M8-e3W$K~D(*7Dc>+P6bjlPQlboC7$NABwl*ka!m}3_oL8^)=EL~p;w(;7)1KR5C3gV_ zHVGFJD5C!O{>3ZS$c4iIj9ATMK5)g-8eUOJrBh!e^5LO{&T@DKDTk)bft9<{_q6TQ zim$)NbH_d|U_{oac>2Y_4<~R?M-A?fToZ2E7)145k7iBa6mvB@28=Zj;4m6EGtjca zWoKEQyLN2F>_!AyWbMEc92XK7il5+8xcXW4GJ9=)`fIsUsGBQYB3!&q4+qlTA)Y|+ z=d)~XAhpE_Ywgv4;JGIw3tR}f$40QrZ}{9UUsi3c+>Z~-er9ss+L8C@q+!T3Uo+CE zE6LQF1b&aV<5nOe$p^WA4=l2FY_j|nuZi{Uz(u%=iPM)*INxc{duM+nZ|KkrD=WX$ zPuR z%F$MMZa7|>(LwnzyJ0!-i-UlQMS`9x5;{p7k9&>uJz>66p|NW2+YpdjEg|7qE%6Q3BxYx{uU&;ow- z2UW$DBo*>nM%_Xb#0@Ng&NPR%Jmwx!mVF20n%{1ia&LP9XkusXvXa(2a`_V@Yl{9M z*p-s)(IlIM1NhF5|CeAll8NHIYYq1z&_+`_Kt?H5F+kRUS7f=kx9sY1x35YYw8hO0 zW3x!VL)hOPhSOs58lHn6&!lU-$cvQe_}%HbaCq*{e33x>D(1RXk?X-l-(4>FD<{vM zM}kiU0AQ`7dFGpnnI9D0;QI+wyIl`Kjd{I`ZE^t3yFz$}|jkSV|CWz_QrNw%CjspPJysWus z1dK4rSDjYb*OR;&ERI%8#iJr9r z8Jy&H#t$AAP_vehJmLh`&HHF{CBT|KD)Mzr_-NBaMHJCH@8s zgRbv@h5tMO#GFIKl)W047l~xAg<)m!l7bbDt9y$M13N7SN2d@ase9|q4356NI=>gF znAo{5-0UQ-urx}rfNq_AC&byTFLUb%KTVeeP>j~>zZ~tYaKOeCVPFnocuv^&k9DM8l2t5>P4sqAh$nBZm)le4==);@8)bn=hU*iKf5>M* z=aga##>DfdW7hHri4hN=qVZ0K_VGQ@4ii0PutBGC$WUbuEnTsUHRnTNnT&3toFZ-N zvJ_Aml-=REkQ5fRu*oPNuQxy-7H`B=vZk zi_QDk-C937WdoF`>=jvWU2@?@;$a8LA)DaQqUc2gn48~fev&TfjieFn0pe@kc5*h8(L?4~$&Z->L7W$SUKp&bEer%I#7}C9pYYkvs29dGFIj~Y|r--!ang~1nWWCoKW8! zHdYm7a;tAc%&~4gK!VxCb*(tNQ+lE~$YoA_J45^>5TBup*Q@H*kVqB(+Q8lu7AijF z&EIccj3w`58b#lAZbL#x1514=7P3L@*EhBii-EQ~OsDPC zj^BzAa}O~qa?M(!1#f5d{Muo}Y{cX#D%RjHqOaY9NkcZi4NtWvIRKO&Wi%*5mY`g(gHK8-52he z6J1BTP<}=X2c{JyR)V=4E^FVrjxR&qaLQT?ghi`zgzCnb zNsCc*GjS@poCIpRDP#D)h}jGaa%g&U+QAY!DuYQ3Ms1wi*`7lyn@* zTYlz&m+od+qeRgzZ?GL8)S&}$Z`myP>ZFUb=XWSC(kG9)^b-d5o-;NuGCyA@|7Y_% z2(@o{Z@oq=$Da+yiX6XZWL&!7Rj@hrkN&RdI>zW{Ev1l?1}-9Y%u-9_!`^xvOmF^p z6`=cDXEo05`SaZq$4?x;`ghF+S_WE2s~RPC7`CB*{Q2pwR%4MJ%^1Ep6JDJGPkVa? z5pMBi>8yS8^{ z2p~cexgc`k$!}Htb#odZ@Sk&>xb|rLdARSQbHRVj`$?4DrCn+Nldk?{;D2Zb5R7%* zEW@krSjL~DJo(Q>`$vC4aJD0!R$8tAkzlmF^6EzRpBnzZRQY=r|H%sct6q21^QWf1 zNIUtU#+b^BlPvF?6+N|zY;lr8w%GU;Bes9J)7<_sVMw=$;Hm~7u+|QIDF9J zzij#R{{8#^VNis8Q9%No_`J#AA_NnP;{Ve){-s{4^v};gI41QkU;q8{KjXE3FERga z`Ma<%QV=L(OI7f;5VVQ-{T(L{>%P-?mTql_hy>Wu;04#;o&;HGpRUK=x+&axi zlG_2+L>Y-OKiYg!WcZYPG%8Z#5ua3rv#q--c11y7bt}oKcRi2>kP?I~U;yuPs=ycu z$0oC~aj^jCO~K0?he4wnenfgOs<*xI&;ZqF^H9hP$P)cmqNl~IUqf%WE8>%XduRU>>P2J{HiEsHbLGKq& zT`1cVf(Ae|jnL;QLM}uU3}arM&`T|upKYcziknrsmgbq{yj$Gjph^}|C`zN(KF-g7 z9RRD8Q_I@i%*Cgta?^jD2Zcj{vieKhPMvRgX2rXsTqSV)O^r1NRV-Y*mop#b`5tmN zueF(36U@-4s!2hNd=ByFAlYHko{Y;>n14M}#D2=0UAm%QoGcB5o%!#nSj3#QWl&xy zQXY!o0lPnU`Wax;TYbCj4kb3_ii<$-Di0}F|LhYS#GCCn=?RX&64J|=&SFKel$h!Ec&j|7CY2K?i(-}o-?^r5lwSkBoLe+p2_NuTn^Ys$WrJB`Ewd0t_TCCzPk-#XiPlZZ@68|3#yjsg*uS8Pol%2}oPHDDJ)5th;opoljvaCgF$ zIo_Ipr8{J3XNnqZsFeCp{RKqjW=^8V6 z9X+3WF-!dj zmg3g0i(-eaikLJM0i&d>r#X4Rpw6eQ(ag@XG~oQaOc%!ST?24XEmb z<{WEf%nx&$l>wz?6#>hbMALw++uX9BIv?)kKqcY%$`V}SV5^bLQzuEt>;ofwysg?c zH!x;3?=}udRRr*u11H3akc5iAbeStaTDUzOf2UdlVv&hTcOv62{5%6&_Zt&VkC+5IX+#!o&q?>&Lxq(8~}&D{*g+rSDn8m)T`8m zptx8|keD?S*!kmrS9feUkY3mrOPE2YF+J=xZUoR?j#r~&G5r7uY=`63-V-WT5e)D? za&Y|0UhShX)7ov1+7M7OXXUFN`U#i8aov9%BlA{yA$6OOc(2$~P}M7GXIh)yGL>$y z0f08s^kIko)`#vD@f7A9Y1ieeyECSt@q0=F2w1?{4Ii2_O>uk*I&OB33&zpp+(6~ex?VG^OzJa z1n$bboA{2FxR7uwoA-!Qda2%}itx{gruwDh?+&DVR-DeYw$WXqoBbhhA_*6|V{+D| zd|?d1B9cUW)$`poIYP)1)`NMoUe!H&xlC3UKq*eS^6Xggf6xB^-J1Y@MgdSHlMQqicNF=JcknPv}oL!r3lDvqyier6{+44~g3as-0KNgP*Q_q6xvrSLxNML^|4MUYX8YAtt<0 zXM|9mzWLds;iRnFe0jDN`KkTYZJd46@}T|V0@H)S#s_X~Pf%7|VZ3W-Zg1y-w|AXA zkMGBm;GULP#E!A#cDGwRu21;3kqVR3u2YBFsGCgL zZL*#`a6d#*>_dT!=7A`w^S9neZ!iSj6imPAWY#$1?p2L2+DxYBhdW|ZcIS|=$s}@Q zBYH1pTcf#Fw%yiVL9+TyPhM8$F)Q;mLn7re8{6ak+mlq`v>c9)k|PR-KcvVtM_R~| z5av>3JxOVFzq4cRstt9(8>bWRH{ce$-WO3((hycSM6FHqZal5(SJ29~;1wA*$t&yk zj7QZoUpe5XmMe1fo2{kxNpYHdg*?~I++|-l_f){D#O`E^PG5QamG|4*?hVlz<$?!3 zO}tTC3qw9(Yi)3vPGS@>5sZ&ZxMgMP2w+R^&YMuxcx#qw@(8v?xp_NrZTs(2>#wkdD4No4n>lWW$y7172kQI4mR6POknJr_H@OH z@CjjvOa;^oF|e(_=H}NyqM$M|4*^EwYeVa&^Z zQ5BeB)Nt8~6$=H@5m7%**I)g2X!j_K&~;=_;uLOrI#FOBVyLdrqSD4PATZRod;$bt zsu!nSZ@7_d!w^T15{U~5_KW#4FmF?;{{a=9VisxTn$& zDpWM{UM|@-$>GXs>)P~#iRwb1;K>$TVM8mPK;WKh?y|As!&S?Ta-GYsaIq%w zx!}0!O@RK4kO61q>FQCvo#D|f5|%-&t|ONAQN5Yl9iq}o-m#sNW9E*5{Lq!vo zPXNd2Hy8aLk}`9w>ZtIQ8pEKSESV+kRE5EHmiD^DR@qfL*V(~Ad2zR4ps8nj=MRr z8aC#~2{zV!qdWb8YeHwo?Z`hTDJ~8;w_Q6;zfLaCi~HD7^!67UFnX^2aJ8`;`wdkI zdH1EAE4YoTUTpl@&3#4QN5FB{@WMO(*7?GY#JisAFQ0#R3HkRG<^ScTu>p{z|Npz> h|L?ETL&TWDot@SdG}Nj4K=RLW`-a|i%(aKl{twKva(@5- diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png deleted file mode 100644 index 720e76b04241372760c78d65c14f24db1153d2b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19780 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_open.png deleted file mode 100644 index 37ca6ec46d2913426baafcd9346b7a1ea1c491f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17343 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testChooseLocationAction.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testChooseLocationAction.png deleted file mode 100644 index e49c41b61f9091d90532759612e60a06e279bfc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17484 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testMoveOrCopy.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.FolderPickerActivityIT_testMoveOrCopy.png deleted file mode 100644 index fe3726f4129328074bc909562beb3d30c13608d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18100 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℘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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_open.png deleted file mode 100644 index 36cbbc47b813034537c9c83edb11fc8909b4c656..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13600 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ManageAccountsActivityIT_userInfoDetail.png deleted file mode 100644 index df06b489de24af7803e5b707798e9e075e9ed8c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30888 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 N7p*ST{Cey0{{hKyeaHX+ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivityIT_openMultiAccount.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivityIT_openMultiAccount.png deleted file mode 100644 index 4af8e9ad250b05cb918437aac3e7b2c23ef3227e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13912 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png deleted file mode 100644 index 3e5fbdf6e4d8a5adb97421849747cf365bfff8d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13562 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 QWq&_uWq%xh^nCRH18vl5hyVZp diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail.png deleted file mode 100644 index 33358801b8fc394a92696ff4d069d3768d2d518f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30471 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 L!QJvZkHh{K<*t|d diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog.png deleted file mode 100644 index de21099785cb9917b759cda5349d0632ae8194e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23069 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 D?Oif4 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled.png deleted file mode 100644 index 0f0607c8692ecaf0ee94db210f7cc5e8a57c2dc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16428 zcmeIacT|(#z9)0L@d2oMEA@14*TR1}2JLz77FATQGnvy;l*(E6H-EjRH=^Gh$CNnZJgsSiEyUJ|c+X^hVUr=(6zRWqC zk>OL0;r0Q6$YZk}uqZu`&HBa;cn&Go2Nmtjyx6?1zH(Dd?eatNHW|_0udo?Z5KzuT^mJ@X?Y zyH59S4gRG@e|q_co=Clr{pX(k2E{*OO5_q*?(N!-_7T5SNC_5 z{wIy%WM$i4uqoJxUind#D54#4x0-aF!bYfnU!eawp?`U--^$nNTE0Dy^1=e_k7~U0!;{mTvdfwhy(FTTRKvmvAlU<{@bHkA-vbfuk?98qIy+#z+suGV$d!hdfiR;*z5L8J2Grf?dwa;<-T=KrVs+PD`&v_sB z$)K}5$^3Czl^DN~SnF=Gyl1LeOE7lYqRJ)83>nyKxRa5Qv2)A_?;S{!?CkATzkKEoqXFavA;rlG1i(UPB1$*PZSb{#6rn;S7L4u%xQ3NBu zP+F~%$Xu)lSYt$P5r?zN03r?TkE<0`kGB*q607a&rCE=7fcIy=NV7{ev2~o(NI#3t zI7n=jL-8~isa0Nw5-we;Jf8*)Y5RK}0x%-Ex2K7pR+mcCmO|ua^s02Cc%X(G*!V9@ zqJ4Djsy#pF6c|Iu`({lB(^` zy6%jO6?|2$nMd`<0kvRKMj@3r) zU=YWo*Bh=#eeQ*|qNUu3-eP6yPJDf_PDbLa&n9&IbRQdt6sYSkUL-hH41f*iqSWBQO`T`o8z`X# zyW}xmdw;3EKo&XSpU`Rhg7E%${{y^X>XsS}Y?Igv7!&za?#w9LiQ?ITahxL@$o-EV zX)PhyqNev6ad$S42_pE)dT=lE=D4)&4TUqG&c4N+->p~dhwDBEZp~1L1Z+6re-$@~ z1(eobC8y|}_V>&&oAlUO|JFJ`KhKWwb%9|6d^Hh6EsyiJ%FGd6{rPU0Z9dD?54Q0e zMRhdM-3-i0-|8|$aTE3+(IL9oN1Re-85f&l0UPaBL%AqQ2sV>{ydPdlSDXM-eekC{?4`GhZmL^NDM#tkg>IPucR|CgL!DWN^tRNx;Oq zzo23VH^i}6{O(G>l}VLN;-#||kiOGdBEi>R|b?r zrC;h#nQgOT121tln^U-QnYx`m-B!vm>fgvumYjQ|2%6oVPFjEdG89zIf95nI&7yvPbTu5zRKwSBn^jlwB3i}n)n5Su zZn0l3i7k|?s@PoICIE(lC1GnmIb*>eV-ZVA*=Ya+xM7z z=QMUnOOY;Wt@hKKb*RXR%tGLo`6X8Kw=0)iQLoejfutsUyUQ($u@B|ISGJ~qX8tbV zJ1np@Q@#=N{#tm(;cjpqfV5#l2L~&$?Sy*4`)?Vcto_kKG+Ecw0>j!xHWz-^(?RbY zPh01m%q`bCU0i!ZEIP^M&v_mOi4(m4F8L<%ZfQP|%+3CE+WQ8k__ zy9-uDq)+>MG;N(kov6UcS&mqTNf^5@S-_YX?K5{ zbURr;M`|AA3#n@D4rjXke%ru29rDt}KS1*%4O5O^@BEHN`?a`V_|4++eOf*<_ zSeXsfiB2!8*bpA_z+Bwfdt1Aa6v6xKe8f4Rv3&gJt`_aAW1>KtVsQ!I=Z@=8qmd;p z(-a4JR!ve^`NGV zT2!Q|OXNyzaeg?=tR4bz7%#JT4#MvXnAOGj`T0!+K?mU%n$c_uytI5j_=?w)I|l?^J0yZ1$&7jw8J5{OpW!QyW}5iv`4Xy{&sTooJm%WS>Erwt z++UqGTh7i@3R;?mkK_;aOnNSJ1xQs$J1c!cleC93wAj0)BWb*mjjLsVnL-9HBMGtU*d=P?i%E32#V$n zC0R5fHJgauS>`%kP&xg?UR{OgI2aYZ;Vy5k{L4e|)F{(%v9GWF#P?>Ck4@gc=<&(F za0>&7DUEpUxj8rC-x<6C-O+HR0Fd!f(jvW}{?TWT?rL+q5id`1NYYzNrbZ z+aqO*@YnVf!cV2h-s-NPNlpkLO~0u z4d-e)*?YNL_>lC<>&JCNYd>~3pHVC%Q&DUB34kgzYzERf%8L$Hb{6?Pdd|M*7`8Gp zJ`t3u^-dNI$Z(pe1X%blg175d9vX}V*iKSpTe*gO)|8rW&{Y`18Y|G1u+N?bcDPV; z3JKD(FrU&m-}bL^vNs7@O27K?yo!;=MlD`thA(ZaTr>FM%$Jk%>4X?c!Q~LN`^xR* zcjE6a@y%xtb$V}3yP3cC-KRH{}hO zln~4Tgv=&VdAF)onOa{8P4`xQ)e>w@c_YZq*Ss&0pFckTlmQP6_1%~j6bE!}tgQUC z9u&!4#Qvph`ib-Spt-aLY>)*!oWskfh1FuB2ppU3mqB_rgj_WDCo@=FGKxGpBbJy{ z^@Nf~E)d>l)jF}%9-yB?AL3oDSY`cUYCgDOPNLVQYk)HlLexsY)wMO@ibM|O zh=lE#eE1<*%8_E!)f>@-C$v~VV!A1FuaGSBNM7QQ;fK9!>8xWOZv)jAv_%4+@zck< zHnK0+OkAO_6tJBhrYtGBnlK}c!t4g7)t*w5-SM2pBI}mZ%5tnfIYoF+jpH~c=zQx* z3Vy^^1K|Q!f9UNMyt-%Ko5e>x&?(+v)(|W=>==3X{N%cHLvf^;p=vPY8<|h02pwmG zCnwX}v1>w;R%VQt-#;Y^(E1GrY?DoFzD81n`T$wECavPu z+b>KpwayC}MQhIf1z-XgT`gsaHW$tgW_3oklPViVPuNHE7y}U2Zdw8Dfx(afug`~i?>H7$CKt6P3x>cDKBH2zytik505wBNcr>yI{duP zC&1hU8L0t}PBRPMQgsRS;LuyFcda-H{|}tK*3oKP;O@;suo>8J-+& z5|1>{pBxSLbkSXCSjJy1HiYC~zaWOpaPMC@vhz#l=^a$BGfPKR|)VMl8ty`Nn^tlY$N%z?0F6a#{woPvC?7B?f= zIiXGQOf;dw|ls67XZME^)zqqyUrzj{W zgy7GWg(n;R)4ZL2B(eIc(f?8iUOr$Eed=`V?8!#oNY36ibb@y71w8=8xk_phx+bL@ z8`0qSBEx%<_SU6uvt~l3)*adaAMj#hB97>iLalT$<-R2TT_srZdZQL7@b$NSgkfWo zD{7x0+0R?dAmzF6cz2B8V3G$#vfaG-?S)*p_+2kXeXqUWHD#~&Yk%QHwr^o7**?lw zH_!?dc~`wTQ%R9=&13q3j%Joh{dOo@>(Sv0*C`Wt7;7d(JQ)vH)d1Y0wR@?HEH@RL zS$(u_(o-gHceXnI1f@fRAI^t0GzTxKy)^zvPPGJ^?~LeMn9c87Hg71%E*GnG$Jy*U zz5^5+#tNT=2!^%Xe?-r3^!WKVP`uFmb-~rf^wIGBgRfg4&}a*{AFfN=fQfvjMYzFt zU60<}L{xhS1lmgcDS`>;H{5fWf&4h%#yw6^aLvSn#>3hrU8f`5%>`_M_+^)=M&+*P z<4IZ8d-v|)gUvdt%|RheDW^mN=g5bV#j5C~{_pq^o5E%9+7nj1Xho8M1vlI2viZoF zsQtWkZzi7xX6xrWGnE=Ao55r*78&oys<@H7ftZ~|lu7q(FLKE5Zw*X2=tRVZG-}~4 z>)qp>`V^tAV^2o423`5MnBO-^VJ}aOK#w)CcQ>hZF`od28c-eXj2&>r!Zmd=R9s=T^2BnECEq zfhdb0lT~yv%X;G;S;6@aJY7MsbLo_LPS8nooQ$!MsG6*2m(ygUebrN2PYL>-kp!t# zE288?%}u~$$Ppa6*c$f0Q?BdBr&%+o^Pkm~D%|>Ms|$wKsX1MohsADmA3sxcrU<}z z6VU6cJ;dbG^OLK)(>g3NYRiN0v|xyNrZS?L=#+JfBP4E--$*0bL5fr$0_9J)&eTw& z^lyeTunYNc6;zt@tSA&Oe?HUH0EB1HhR1RQCn;0IlF`Z)^U8}BKY|=n8hkgo+drN? za733$VzP9;PAyeiMi$uSc6v`jkWJn{&5c>4YhiQkD#>Wm?fK6BzkcpN!dbfC0{)4# zwmQE%>mJBv0cKSO($FePo!6}tKH!E&&o7XPnY=5cJ?$>SnRB5D?qpI&6 z>n|&WGNaNWS^dPYb^0Y@zN>M$32Wh((y>@O!l!J#0Mut<@}d)e(7ghAjHYkw$|d4m z3BlTAbf-t{Sk7mIg&G^}QYieTYHRHw&cG!A1Aqq?4Utv#B+BF$y@YktH4{1CQ{L%H zuHX!cdcW@1-S)xr+`@*|nCTejR)wu8FtUFwn=$+y27Utv|2I406D?qA!%N# z({rEK6Jo&K)wHGTgBin%5gSBW%wh);YsIS?cxZo<_49_f_o$Ik(ALFMAAJc>-6J~q zPI$I{VMN)RIarSK&@%HPQz%@LHs~A?tR%pOM$uKO!+Gv1{S%8 z_Xpw7!?6{-etr^nXM|eXFxS9es+GY8V@Miiv1dl5rh-9`LrKtbra}R+t@v;@WZaBD zmUC2OZ{nGUg4MMj)VEm~ki+A+JH*XBLD$K-Ij&`w*`$EhhaSS5ug*RsFM|ye)2vdf z=X0W+H0qFQ&VV_3RK^)-ziXAFN5a`OMIi|Lb^{o_8SC2!fCZENRhX=CYwpQmj~->v-2*605&j`pm+rjUG*Kxn-^F|7-Nwfo_cRLQ zayiAs4EvIJ`8y-&VcI~6l&wG2I6h-%d;7_dpm|Ys;8bRI;1T>nW2)MWh3@T^U)%m$ zGLw*v^5r~Pc zNIT6qoYdasH~J#SL^gM8?3AOMb8Q>v*OaGr;1nln(DhXAFcscnR)@P^j_#HPb{!^rY4}T2C$D^|O>H4{XTZ zhnK!jHy>*>eBxhYm-$rAcdWN+2eWauo!W9f-1%N3mi@!=g}c%N27#A9W%DTNi;W8T z@FV$>P=XL2mMgbgauBW{90?PMLwXa`*G+43AvLS|NG~S*NI9a8%|~+XmUlDRR@=w! z#fD%HKn=~jBZgded*}1%;-`yJ)90-6RPps2svL5`bJ4nu%jKvX)KU_cXCsJ^E`GX) zq34I8!YJr+)N$N9tpRL3YxPyRs^uHQYc)%^?=o|GE)T?dfKk1OuxpW|m(y&$xz8q6 z5G#MtmfqIjgS{sTZ~H(+;swYWW9BNBhZnClZTw7hTpB`$1Nu2un)aIJ4H((r@Pe#1XVD?h5%P?-S2*U5#+j`1qCJBhxjJ`VFUXI-v z`*CThf>a0gy*&q|7=3CwzWjrliPiSv9ra~gUwb#t^YW2#cdaxL^T42>0cUMrZG2U} z{CUAw|LNA&R+@0Qq~vpnK;F7t!BkQVF3$MEK6T)iMB?Az5ABV!_| z6bYDjXJIF@|43eW?f%^}5B6uQNP5=iL#}>DvVK2UufE;-wz$2?d;iat$qZ5D5EJ;~ zOGP?^>rksP&o7_7fMY5zkhv_Q zeOk!uhq`=$;o$TP&fi4E8N+p`_(FSBtdj`b{ai(=_`@FHH+R=?3@dh!IRQp3_1b7ZaXLmWm7_Atan9DE{qeZ&|mY)&e6M&q3(uXntERlgIW zU-5#xmypEsYdNTwP|2*|ubOTNlG=OqXNr(D1{u^zWQ$8IM7X z8bvcG$5`uya-R9S%p^`I-gtE(PF`^u z+_on=Tx`I5>r8NE`{>%bNAY0)#SQ^wAaF?DLH+$|MO6% z*mdaD-a>inQp2|bMlykx!?uR@6tuI^25x}|?(R}2FnEr3y#4_)Agy~}*RErES+r5e z{B}!2@a>}qF9G*h^^1-C7MmT%S(#*P8pVw0v*9f*{i#n>yS^Mx?g>NL4%5=5JdGR* zBU~`(Y!TO)%S>k|U7zI|@UFzj{P(Nf%|R3kLpSbo87yfHBfT|uAG#cAD2kV_5AW4< zp(~@9M0GS^e1&|Iqas#BC;V7(!)P&xXKGRDaDoGna=M3m@1>?^u}W&p#3nXEb|8Wv?zFUVmqF6BdBI88?%(D6!bQdYaMNB z1ih#Q5~*aHz8aNV@M~VY61o%2!eN2Qnk_3HFEJ>G3u>oHBsm(F_g34Bf{(T~GwfQO zXPXUFioNZpR{((RaCHQ}D#sVj>L4%9$;sJ~;D37b3%xagxOmHK@a7FAlvKm@mZW$t z0QOnUn*)_;>)DbL_>Sh;^T6G;w4OFY@dLoxKK}AX0aivi>mtaez{zW`wRK_lzf~Bd z*f=>{N$i)Rq|t7qzJ@AF3YRbDTm9{47`3`sd#&5{%E4KXSAhkv%=(lVI2&@oLP<$0 zF|Y&0oQ=yeFyAeae~Qa-ZL!c-W}!@#R0(=uT=I%{eJVtNfOd}2Yx$C3*%^_Vy?%=$ zIF6c8SVcxyTcO2qTxjW_H7wkEWBPcflhJ(4+1I2#FfkBaQ)O6asBSZu&cZ8eS{;a; zD$h`1SzpIC3>jYgCD=U!vD<7qjFyfov2h`A*qYx$mxGL7D6&nN!#o!I`WARwl!-~d zzsmS(Y6B`{q@`5?X0tvQUC-wC)xy+x!lH+BJylc0>?5cQBn`SqCz#}JvGwl&lFWofR@xa;m@+;VW_Kn= z^-m{pu~hdNw57pjsb=4^d_dU~3B!q{ZJyX1_nxCDeuKLjXU z9DT+3_W%CAh!_Ym++uk2>Idn`^#<-R_P`c4VY%M8wRCX{LZv!@FDgx}(&(Aqroy_9 z_*_w&SE@sbxFgz-TPKwrj7s#+((L!K$P<=-{F4aeo0&l@68G;fB>*DRr96_4SG{Wl zMpUtM*RQ|K%xJ$yg{0<{hHRM{e}`aJ+*6J|IZx94+0qn@W5~(x!P)&^B}ZspJE}%A zYyS)&!QUk4C~QmZE-yg&>9^-{_-2P1zvArbeD^h?wttJIiRYzn;Az}RMYpz}m~%4E zR2d)j*}V~P;@dkvhI4g=eS^;WW51QrP~YtrCe=2Hy)C+lLk&K9svvu7T-U(T_+?V! z&kj1cFufKc`I675% zF1)1Ab@p3%CX{U|A!^$n7fmXnvbXI=Y}Af-7E7!sk(cG%eqw3#34K7{uV4=ow$v2yc=u_*LHZ;&(Mcf4qYWi2 z`RVYS1t?{Sp<7!wejuA+nnZ&K@7yo7j+?3<&ya&}ZPod-zgJD9S}kxSh&xg%!9S)+ zIBOg`cbPPp1IGC4IqMwr9TNL0_n_e6N@ZY*s4ddkNELF{yvBj+(uqOLLWTnUb`}N& zn*t6WGg|NCM+{V={{r*jzI}Ubzqg9gM_8=eE$31^+o#Y8dA9H9I9B#>9pjrYUGI_d zd_G0g2DPM})+v=%Rro9;uV0UCq@}g&LqXqp`$eDt9qZK;!jo$%r%Mngf~6FuPyIu< zd;niShqC3#A=?)(g+YpY4l)y%G9bdmSQXv9a5&48yzH|&@m$*Xe0pz$KqZLWAG5s^ z_1AGciB8mSGel*&V5fHG!Z%e-^UAIV9ISCGoQavvIGtqpYaMPMZeX!Q<}>*(%-dB0 zrga_FLJ{=Be214efT-!F07)i^H42j|pQcF$@6}O>e0rdg9KT>03OzzP<~ES4KP#GO z*d*Gj(GiToHeGMaRGAnfBPaT9%?3$(DrsJSubTC4dpt6JW2JfVh}FNl0URe( ziobp7nI9)m`mTmjproN$im;T{t@A_8&ch+Q6*yy3h>L{-X!e&5J7zMvC+4r`*C2nW2 z54E5kZyC6;$0TZf@$SlSwK~qO{b4P>H#R>X_Wu2QwboE_PVH2&K)y?DxJkd=b;M$+ zHDnD2!IE?SgmG5`JapLZDJ`KxjLSI4kDqwlr5RKy&9;SJ@FR{%OBol~@3)?QZR+yWipN z>0t}0ZssPP`y#$*Ctj0Er7t6SL+QL>&BxrsJ6%u~enDXMO_EwC=c_5)hb_qZ2Av#S zF`N#iM}Id7FYQKR)m__^951%JcS=*2Nw+--1rtu|hU~5Kj^t@QlqGB9z^^>t(&G~l zI4Icq86y3_%Wt&6!d&HSFJ3D}dQ2Aane{PL{}Q)2_V%l4yNbd+!e|*+DX=!70@s~} zekTN%PaSW4NkWYbm}d1_KMRes>Fn&J{*i-kNuFddA7 zskbx-0USF<3|EojB9PHX28D!;W)ROgcN1i4#UZhmn z4(yuNIB>bRxGa^sWxl8ceR$7+@dAFJW=xvp(GhFi>mJU{BWN_Z?|wYKnAo;jx>y=W zOQABo5PV7R*O$O+m7dp7Ny5ShlJ!aZr+?QJ;oxlQpRWUFpr8nU>P#I}`F3$1!a~a? z#eRN%p68vwu1vLCw}BqUT9WX@kS}G)UW>J=EWaH50iD1~B}!gE<$@@GJMji#;3DMA z`#CY>IQRYgkEth`N#_rn0`}tq4zKi|^{0rao)9PUFvx)MA?8-NoUhJQt-oSh8>D_C0`%Bc0Gga#-@a!8t9@<&XD9UI95FB}xjCyyeFe zH33kjYP7i#N624qf=<6RG*k!UZg>(mCVp9Qk`Uw_Opf~e@0Ca6<>sPTDFzZ|7U_;G z-NTQhT_Bt$*-6DorN>y&rwG|b@~rMqk|KtuYN`L$veV?kPA*EsfAub&IB7dPzrW;@ zU*j@1Op~gcDr>?lY9nU;%5!;0QZQa5SF;%1ZGI=F*r+_|l$@%krr3CKVjH(TWdw1v z7{M6`0^bYMk?`RDl|G)bJ#jXqw@$@g4{hVN?z-Rrg96!1LM4+~E|1Z0N z{s|%a&x84YjreaF1!Z+Iq`59#y0laqA0MBz?FE2kIyb@El=pFBc zA5OuLQkW!&H^CJ8%?i@Qz`$UW6tLI_;ML2jdur`Qiewi0xgPYQ!t1BUEej-*#L_;= zd?IS4e9-nJ4{Wa*&6;SCucMi#m6BsR#uv*+NY2A|L*Y;VO$JQ0h|=&;m>Tm*l?*b!=-=L5#a8(lC5EJgieTqKDiccU*UZWxza7!YCr z4u<6}edHv^6O>gh$98ijHq`2Dss?@HTI8+5jlvjqMKTH`WGP1$Xq?AYpDf%lNLf9` zM+}#=ce9}ovw?RAgqqEeqvzb`=M6PZ$GgjSgv&K6->gd;^sb^`(xu%*&q??XZ{~^A z(8It{X^)?)oF8THi%(YB!s@GRG<)N?EYl@|mb6Ghq;B+4h1Ec9l~dS1OGfhmHV>5P_($M%>ntPmZ*|4qaBx??-mPQ#)kC%fa=|@QLUv6*{WIjn4zVA=6 z7|jy`j3N7LydbZ2J_5QNrYqwe_qX6*Iut;%K$V=$IvBh|IE_rb9~uV*(Gv*5W_7Pa zv(;kTMEvJDMay;I-W7v~O9McW6^yGkxx9Ou>+O5@_}ESl6-4}cZkkj%B<5(Phzx@F zx3ceW!n-gNH>XnIX%? zq$9D(x%zl0YG)RzAW}*bv}9gpR==G(`aD(L8ikTPnd zG(jcjI8oSmhUOr3{4Sq0pF#+IYDLq`@H|8nFq-7@AP7qW{%i-<@9^j*S2FOM8V+Vq zhX}xd@{@>=mg(l8?&`<$rf92fXv#&Jq^k{|-5KH;;J5T!%T!XH;Wg2m`(matx)fNG z1X6)YEc&{+eHxKKAfmhok4Oa1k~_9Ex1rOllBDz1gbBm?i}nG9mRu%JLz;7R2KK9d zP~T!DJq%T~eAR`k%`Q<&kipSHG=^9&t(7IuQ*nHJl9C;S-LJA-q}py?cjha;womAN z57IS(`yQ6TrTF9nHqxt|cR#-WASVJPRFDS<(7!RW*xY0buCPW6m}Gy2vFiRL1vzdC zES(9PucxO$<~$p(tfjfpv!kavANQC_$}7^fB}l!z$_Ec7Oy&^;s=QO&Z_ou`9!Vn! zF26#L9cGicIaJQ=+BAIw&ZL7IvD>U<7g<}$4tHo$n~{yZj^!zCJvn^e3V+cgLy3zp zPC6(__nC5msdf`gYi7jpGY)M&%3e&E8w!UFv= zMZ)SM(>JKS>5S}L;(li&W67>Go8AnGozz@4EG%8D^qUeL^YXQL_Ct=Th?ZL=@S>?7 zRQK#74GVXO0MG_mNp^~ASETzCyo1lzz1l;P6sQ?y2cJvzqQFx3TqnL|;;DYw8aJV< z@pnoiNXbh$+p+6^PhR@A_0p5>US?v%s zEsk+ZY z(qnu)%ZrrK(afwSX=s@ny9bdHNhZQ2hoGcUT|TjWB*DO)4%v{leGQu^;j?FV?6UHb zz+?hOCB*8=c==@!cxu6T1K(~a3lFF`>9f42hVcBoOLsh~_;4m@T=ru8^POC~UzRqk3N#lM!T0v71S~2$ zoWgZhA1svCy6VZ03BT~Oi)0k4o`l}?v0}%vNqBua zuKH>F7B3p?rCi9bAM_^k!$vq%x`iHN-6A=F$x-bJMXE<{Ud<$`}Uf{zN@r92iM zUl6vVdgHlM-g#nKa)ceexb!~*iIOC{UXM#nE85KLH-G;w$cPux8TljhFFPGCrHQ?Z zCg=*x1T8z;k02BA$l-T?VI-i)C@;57Y>g(X?DfjQle5DaK;}*~FEqn;s@lGg=`AC# zY*t?y50vCu8Jk9r2XRsmdt`Yn%7Is#P7V?=z8h+#_rwP#0YokSpc_h~RHh+U+$|FT z6uP6-6gUy>s-}Aid|2t9A(Zr3f)zjATgFGSd0H7xI=@CO_V_<~FFySi5<(IdXVkHI zFd7?^MUfMok5 z`K6s~{=*jj4_du{Cd2y|UElw*$@-rdy8o-|`+vjq{=bpt{=*UBEF&xHKA>w<`V8n)yVr2Kf650s&*8up&Cgq zBLmv(4slPRfU~myy0+wtMNoD9ux!7=PjLJ)N-w*;oJ8Pks&!lEh_~H+q%j9RI5^lr zIkos}a4W_ejxm-pdG`15{ugcht1m#ymB6UmIccu8Ix~*J!NF1f;1B|Q z!150+L%PgpQ*kBd{CtS|O=4omQO`4NkM}k*ro`~Zqv^u>`sAteJkUNpJv|%2wMgSp z4?R6ogl&!yW;QnI>)UXj)5_Ro-n*oyT2YJV%0;dc5`<(xO{!I z92OJ>9+X<@>w{Yk$43+t6iiG^B<14+g(c8;4sI5IHZr1IZueSu8@e^%ppW~^!piDf zU(ZR%VG?}Hh*Tq+08Fk^!kHQ`Hq~-%hUNsSXZAOxX_^$=+ypWCPvKv`zM$7``2_2` z{YDO%kQKdHwY=LIi;A3s1NVHZ`RCM>Dr{(aIvDq<@daJnbW{dcneAU)oK7@x4E8y4 z8ab5DcOvfY>&x+Ky(=(P!wQN5#jh&Vm?+Hh<6vXIU53=lYJ2d1CcS$+X4~1{x2MF! z=%Ntx@9kbWV-s9BL-V&*=X#)^IHBhgX;WS5#*gpqE%>beidWj6y+r;c73{P2n}h9R z>Sw%lVumzLLpOeM@>ji5uHGpEFcW!lJ=1=${e9#XZr6A4PEJk>-$Q~|qanfyV>6EH zR3m~O$Qj?zOOKmLu<5;jn7?~gVPo@abrf%bOrUT|VNY{PNCJ`C6&$0<-L#;yMXti2sVLP(ibR~w`rBOx)d*mMC2 z@w?!BxsGH)a@~$fy{u2bhiG!~jc5H5(E`ofA`e+)J5EOvL{ZVM*iFSjil!gkH@#`R z&K0gusv4_B*vL}QSc``J?@|cbdK)p)XjnQcAM0NqO+hFtIy!M=#GJF@c*~N2oMf^* zXgm=1R?~`YBDTxg`Do5(XUD|h=r=)z*V((NhSH7#7K*XUG-nJfBA9wd^RY}Xn1_dT zLv?lam79TeF3&CYKyNk?aA&jivkrIK-ei$^8Y8gcC?Z^Pzw2&0eng1meZ%Uh65LJs z_VZ^pxE&n2cc{;z4j&10j% zSRZJr>y78tCl5i>Y>$!Ozp=5XJ(wV5b9vIhUiSe97c1;tP~?h)kdV))w1148JiM31 zak%2`?^hVxwhfFBpOux*x6PzYdEH}5x*kiq0=iX(AGvALkPTGv$Hoo`jX3c zE2wM8{F&2PA3jKRZipT=5TM_TZ@m*S+gqi$sg;(*mQ&K$4k);Q#xtbsx?+&8z5Q7d z{24>A=~F`RP6TR{xbaBq?-lvRBNEc>M;YkWf>|&PB%utBjFso*wMXaXx|zzS&svIT zm*z>Vd^&af^;UQ@_>!{0u4)YCfn`p04`0v~sII~O2J>xjT!*Vs!3D1)j#(HrKn#Zr zW7T0OMYGsTN1Gy8;0}c`b+@YrZ=FaZWBy;AO_)P94n)94B>B2Wx7d2^=V?B-Te(Zd z&F;(`qTl^wtWW0&4XKq*0(wT`MMi=sD1Z_b?WrjVrkzFXErdvL_|#WdsAePs=cV1Q z1T$5XmHLh*SpYeoM_1SB=wWmDCs(m+m2x-7nLAQ_o6iTo&o!G1>_q${6MIWOq6W|k zA&R?{TsMWkB_2&K)6>D-lqT5TX_XW0vN?9GkS&dJluF`qk-gZ$^dZv(xgtb{PiS74 zhlxxCsL)6yrQ{SZl^O)0)=k=7EGcciJ!~ zFegBq1&qKF=R-v3!OAttEH=3II3cBZ?7H;gr;N+aRj zqoxN&cN$#S=DNm(SUKC6c3e!y>4C2(4)$p5N`nW5x@`3_+@$v}a*Pu?)kjAHu7C2w zdSySz`{5cl3_i$m-icgZgYld_?)uSvslojkED;4(SlL{vG{AfH3Wmk&0sqoI#w#^g zDvq{Nm2}WY3H)@63uXkBmyq}NIE(+*$?Im4P+&y zO1_jIO&f`MoiMMiB5Vfs5g%Y4;_Rk{THPGgbAAyCr*o%6h0lP&NB7&-2cwv!u<)HJ z3-`4&tzux8y3cmk=JPmMMCC~Jlp>-jkTsKV2cik=WO*&mz2A@Bj?DJFuV3|CCW0oE z`-ed-)KyAIb(gE11y#$n{1~-)R(E6aZ5*`&b|>;P-6G61yskRBg5XgQ#FZVeJz84F zic|(M49@W$LZT^xI>yIe^+jcLFEpNy6&k?3r86RG(|_L^NtD}C9{?gsy1WOqsFc!L z#8uF#mT;+-sr#`QNMy@EsHf;J<|Z5*ROWn+TV6}5o_}^}k!6U|iZ7Khr!LVrgk8=b zmG3Cxv!Y{oJr=RKGowgMwKmugL;YN^w2p@C5*Z`4|HJth4V{1>B(Cfoj$8Hb7#tZH z=;y9LnZ%pNdJV5S>*Xj6JR-X0(zcyvjl3%HchH)ha!P!Dv)7wl z3SDX3{or=+(r;!AhBvlefwZLKuzqD73hFIqt9qJMud_wgb!V$Kr{)irdq>imffMqc z%WfmFC2;{#H&^Ko?0a3~{LoN=2M*U2bZLn$QJD#FFJm5Z}yyp!B$LBy1-;AkHM9UWeG z*|>t4bM&lw_ML87Dqk!oJv}H_D%awt@ZkE%i4FHJxr@9B2iS(Z7Z@;0MV08<5KzD> z{kdBErb6e*=8;P5dr?RGQzcy0TqWAk{H)amq8qJS1Sw|`J)OZXB{iUK{656#Ntf$I z=M9<6-`?1*v4YT2NWIZ?ELZm4!R)!qy`}N3M_ds{)x8OH>+t5n+tUzlYE4{PLALOw z`FbaQ(^yIKT_8LHYMcR`INQR46zEW;Tt~Y^vyl)64&@sIqS|zv$xMY0NHioW%J@*& znjHgCLTWSpG`KkY<@fm&^a-!Ys2W=ARfWkR>Cd$8l0=Xh`k&)4H%cj<=}aN zsA%;zlp-~YATy~%WtR8CU1vWp^L@CsuzV`GC^YI?Y^=8F(Go287kC!W*EU*T!nAi^ z=zbw3BYXMYd~Tr2$kWqvb6-3ZJ6e)+IFTEP*Xc;)`?#Iy;vB}~-DRA%nue#U$=>HE zOGy3tqg88#(;;H4tbKM);?>Sr7mN9nmdDIK)@>?Ri_5`GbjkZ*#gGmQ8NF{TLJlaX zlQl?wY65(k%5}Z8qhW7&qC%yRa=a&~Xq3j$>%oT!>#$=X9SuZHSPHge11-2mmU)gbsE%N=d1o*-m%UcGjw99lKmc73H9fre zV0nt(iqIXATJuI^?!~|^zrZ+V0=1sY28``(RA{a=&P+p)iEZ!-jgdSE@2E^ts2k@k zCH2ot6VsiZxQ^Ce6ekE=0sbzT$>Mm432sQET^=@MF1)H4_@M~LWfB@Oh;I}*6co&h z9wsn@cDA;vi(X!p{6Ik=cN0c739~9Jd9~r)6}9it7MS=&uc1sO3m&bbEnB6{U($Kn zF)4k2qCg-VC3K~~)|5t(9Aq29LZh*O*qb}Jx>_ww^94JsKUh7HKtzhwPfZN>G} zp+|d6CO)Vfk_BpcjExr`lUE*h4%BO8bph>#d@U&cAcnj~u@;T5fIS+fEotkQ?XvaE z)B-LQU9_{#iCX7Uu3RzV4qg(||738M7>Lhy=~2d{maQMlQ`+gkJ13$ren!>1Q}()l zi*EYNv1zS7$*7}&RP^ABv7J@=Wewm zp)3HV)#Sev@kJ@VlK9-fnckE72_bR@v7&m<{~Qf3N3l^Nm>tn)bc}6>%LVyJKmg%- zpOL00!@qPJ_h;o7=N&w@jr!mn6x46x?X~@rD-HPT=-Mg)g*^&8on0SvNkk#+UB`WL zTl4HaLM@*&t$kCXu3oqe1S2p7ULW+Wt@I=bRT{=JY9RF1sk+z=z$oZtnJ6_Q9TanP zT6=q!m9%pCOT78n*`2&hn$4NG*DYj=9p5k=)L__LS4KZ3PKnybK+ow(d$CGZQ>uz1 z(9vU2pZPF2Ep9%q!^}e8WsQts zl#LkDX*a%XP68GW!|o8<=D=@sM|(w{bH>hKJAk3$^1I|W0ws!0DxL!0!0SqQF_Ty( zGYBsQ6$k37@aTkRo811`2VA(Xwm(yYmzaBy46>lH%-!Q?O$Mj4=QGaxvfxe%_%ucD zdU_>d@z9or5vBXUoQm7!)swJxpS`O!->>567=+(l$&pckJurQUpY~>E65zIwF(aYnTV)_zExYM#(rX=FjtWX*Nz)epN=pH>Ldn+RaZA>k<+215NsEa*zeN zr@>nDJzmbXAy@O{sD?C_dOX@E9(u7{#a!mj$~Si$R$pZj%GQ3S@p20g@$(40EZgQg zRbg{iW-zBc9#i1!G8&}l>M}G5E*=dn9vM`X1fy1AUPIBZPtesyhl@+{C`DEuT;*I+ z&%b&Ly^x}Ts#ZJ6)L3sL8($J7?jbBZiRgRO9 z3U)xs#6K_~r$%Fz?@dsJK_82BSIl>oTG?e4{wkQ$RR<;+0Me9Eg4@1!fbCgFqDmBH!F0G6vL zqsqQCeSX70e1lqZUHcdyC2XS9bsL==#$B4EyT!ed(U!q$X^E972yV#;ZHQJK#0p@a>NxR9{{rcz~nTEnFqa%^l1c!B8N&MS5;YwC^q5iP6ADRr`h+c~?9V!5n; zPXdZq(|CfzL;d&9;_%s=-pV+b99?=G+@8*qPLOrE z+jys?t)_Q(@h(Zz=1*dxWu(F5c#x_5@Dcy-iOHw@fR_CnnseVb_LHl&R|~FMs+}S@ zU?7g_-BU3i6TT(lce}vH<73J$!1Umbo@}Xx1lF2Qbv*Vf)L1`rv>dw|G@jKfS^6cL z%bta@-d#Q7+g4UqX*`KPk8apkvQ!S@84L7!^GYo7s6Oe%GHv{c&!3@8 z72eM;0U40$;J{*Y=_;I{+UEGlGhza>`#uPTh}`@7q~Qf;{Utt&e&Sg(wbS8DL{=AU zSJp?Y63tpFZ5IdW){xlD{DZ?ZZhO@~8$*sIx4)dgt@>ul6TJeUmRw9@hJ)X~ij;jkS`TF^>xSmDr;fj3zX`jmH zNZ9J`ooVYeSC?=wNET`F+cAYI5YE)*euS7COmH@Re4!vY+YBi@ zXms?vCT3TrojqTcF%KWr9oI_7Pe9Ast zY8pT|se2%t0?}M)tJ+Q=GKEus@Ny4^a__jJ4>1f?YsH6a`@+ReDLyj^PTz4g+ZosD zyipiS<8zfNEa;QvlMFAOeS}S}shrm`I1-+XeSmeDnoX&)=w6S&2Gbxz5 zHH93{#)&!@?N!NOb@|mCSxO9eEkYBR^4;6KTAR+E3zfV(0kvSo>jr*n$W}OoLl&R! z#vIURjgwU~tNK*iJX~*^8^lZ7bzfLmn)mmS33o8dv>rEcnb9}5T|96__4LntXjLnL zRt4LBtvF%6botoZ$NkRPXE#5Q$Ujfi1icTQg?@foV*dNkVEHR)@poFglOgMxzS zvUFsL5wnp#VzIa|(5hEjN#*NYa_3j2IE#4rP8n6mYjVOpalfv2EZ%rgx3cN@PR;xL zU@&W+Lk1a5@%WH{{4JOA5klx-g_H`>@0#kKaZ2dva0S`mQUVy2*iQ1(C*))vu9ap4 z!Ov&0A59h-qLE*n68Qb*uL+-ERcI^m8HL$2ry5*Zt~U0nVhs%q@7`5KF-?sTc^NRoNfsLn$G_*-00 z9W-lgyVg;AxZT#D4PZ0e5pp%00kYr0dsy>Fq7aJ8I*X-9x#wv~r)PSd8tl#3P*zKq zV4<@78Vl7@tq31Ll5e&BdphezFRYxo&Cj<yxf$ zWWx*>h|h*=(aw~ImVx>lw-m_|`m0rvlRE-ZZv}NEkL<;Sqi%Vus~(wmcG~Q>;{8>p zjnTCpG5xy(JYQ$kcfaVScQ}W0w$WwXP84)|j=Pq#l+n*5Cr(EfPRuKn{_VZhg2_-S zi==8V3rpqB%&KLL)p~)grv3|MkJWBGWskZz10;{zO(Aua0#djTwNfpZ>#ieaI&&(z zZ!NLh>-K?x?}8S3-^F>hKYId_JHD$x?~jjIo9D9(e+CbWk`GX2Q3KMfcM@y& z@|+k}Qufc0MC&Wjus_u+m)o$Ppjf+}WmzV$-nTlMjwbl98ZD zU9Vdd_v=TB>42D+9KI5D%5?=(?zDOr?RnPVvf{rd_x)3v!^uu|$o-sM4eGLnYQUMY zJ6^~@_UByb2Ir^D)8>AUh$)4cWzaYEDV*?%cki*aJ>6~6=uQ|cbfok5_wVd4Fl_SD zlbLWkZgs9SoxnvV5)cE|QZyliE8RDxyf+UO%eDL99@q%jL{|rk(PiQMvHWA;Lie1x zt(a5p$uZS6W_jD11VAi?xhS%#K8P20teIRBhn~nzGwyAd)4F$4HI(J7R#H*9QKBlQnJ(%w-2&Lv5H=+f64VXiw=ol5!TNkF^Inam&|snyesaWZWTVm7gm_xVGyuZaVq>h~O zTOF#x2d%|dD{M!@Ny+g8V>L}qhvDyHyrZ|(Jr=epF7eb36ncAmi|%XAbB5;PMU}1Q zWSN0jtq9R}C9`~-KjYNa$^=s0cuQn@&ujR@G*w(YT-vBsGHvn>`cX|*^?byPK#bJb zQ9rY+iZ+OGo}Gnn$LK*J46pA83AO(95kEeslW$9E&`owIR{7o4A;oAM4f-Ho_sol^ zMBod_lTlugx?deIdUw0RWLs-Y^INAus9^7^r2PR0@`4aOj_1E{O#g+y`Tx_>%_<&- z)U9N|z%d(7(Z|xNgnGLImbB!Qm`OO8SM_P>em$zvv@0-ykaS>RAUGsMI0Tcjp$F(z z4ztU{4T~nbb>v*F+3H;cQG?SXk5@V$f&G_J#uL~3d$`ZLRk<@&mPD^$VDv04BSr=H zgS;}X{IaryBlA|*&tKTu+7@WmB3@ivI3#;p38W<40*_sWZl|fZ{A*sG9VfBv{H`?c8GRmz$!FyMk-K97F{14FEoZB;W7Q}?}tYrVi(GlirYAsj3O4b`B3rjICF|+GMmj^ zQc}Vi@O#($IqsZ|jV-Q}evC5zusR#*uq6ntrL}eQL9@;z%VXrz{sS@!%Bt(?t3i!; zQ9x?jFeldn46k2v0|>CNu$oXIpobzvAlkLlc51n{*cJ4_xa!!r^H%rkO6O%Shl5{W zDk`c5w_UrYYs=XtoSfcp1wlsbChT}d9df#r5W$D9l2PD+ZOI4%lxZXAcz~pZ1wAYZ zfv;!~T8rH4vU$5i-d#|bSaZJF8EFZE@Icp9$P{rz~bnTLunVb4`Ltt-j zfAPFWck%pS@8~l0@aRl=z_|7ulh(J6Rk_S!*cu=B>?g26q?+I&{<{R>gUaW|&o3py z33HXp{5D(j(-IYa-p#jF}*SswvTSuOzCIF!#A^glf_WiFstIy`MLpS zjPzT6X%s#``tAIWPDZ2v`r#+~hjH_IXg=sQ`(2S`acW{_6yWba z7~fMmUq4p74fjDxDt0u?_S?dEEi&N2ezq|bM&=4TDq?ECc_sLK5d#8wx6ixZc#We0 zUKV3^PL4vQzWD9Z_40n8sZxIbviNiy{@0NKZnEp6MI{lcR}*=kI>L3mdw|8fF{TBn zw@z66kOX!36shvSW%?btnuya%KOzMZfBuWEET1tUlt$O;&(uYOmBa->YQMwuL)A*Y zMHQ=*qsqyliAhLgi{4+cf|A*-kngEvD$It>HY>BL@O0k$iJ!46>Lmx6?{Mkj`K6uQ&+Ne;~uVXi(L@+D`_O8T*?~W_sE!& z6GJxyECLiwnNG*EFe$VuCl>=T4(p_|aXx?OjUO^8ob7fUWWhOkQ{|3b4*^IkbzZG} zU0TYWp7$3WI|@0Q?%nDgEsgdpMsw`+npI)~YVA7w0Lbvw?LZj0DooViS*r%VuFh$< zQxrsdKYyXiVm@L)kgf0L*Y*TtAeU1&vsTBgVdZz%eXMF~uKi~#J}{dvt}ltEji%!g z^Yd#pB}LC9?y^jc4lEpHS1!J2R;%2-nIfw3t~qW`Q3x@!yQrH1D{5uPb}b` zO$b%tFRM4XRD!th7KH)JT>&nLbfk;Reh)i4#~^lE$~akaS?-Po=cx~n!UHQSSuQRw zs~-;J1fOW4LNWbTvnjuy8j8zg?4F-W?#$*iA2eZ}|H}O{H(V$u3zZ}vn|C-|ReUKJ zDmfXws>nxP4jgWa&lJdBKnIL_ZmFB%Ihp><+&;Q(V8RSRBV!FXDsh>2P2ySx?`H<*q{;d(w{@emQIX*>3kuXuTvyUgIH5C1}z>7bY zgMB4lMC^jcU$RAUp!~e)$LqedS4nNOVr#&eSCIsB5sI@uB_gJr*hN84g{y>0+!68h z>#K|ecInoww8)ULAM6&vhhC<#&e-2??>W z+D{$NMnv+v$8hFz-$Fu?mgcIr4NOYXxU*Ymhz!%g!@$Dwt1oJMLoG>-&7{Kz1v=6? zwpzJ8b@+;CygUz9~diauEwZtx*W00 zFC+w^v#YDpWq`ays|`_ARrTx<0IT{^1;2DDBH&U`P`J3e=jizbCLUJ_?E~P}(#{Ed zWN7br_4f0*9X;>95AIalXVoghGJ$r}$1f#1&6-Y3GEb(6n!L3Vc6O{(a*qTrU%mv^ zToUr@QKWQyc0*FtQk{@vs!w9T8M-y>>{D7=ij2+A&rkng|5BBE+^B&pfk(l>`5}%rg`pq(X=^7bP|%NyUV?tJZ%|}fSr1vf54^?YZtDkM$hZGcTqcSdn)C!We^gA+uQqr z=N=bH>3rNIov7g8ufV$Z`Zsjcm|HpYgOu#lF5CMdXtu|@bx`ZIsR{lOKiX8`Wu@#p zB^|iYNpJtPciw@j43I42C^XhqtIwY^y*%&ZiBFm~-k?RD&op_`T^-FMP3O|d7ppQ@ z4gW!W=r|?SS|NR?#1_SyJFU&-i=Ddpqr2>$wqrdDv_J^;Ajth5;L8eWGqC&?WQdbH zfBljdU}`E5ONhl^Zm{1*<*=UhpDLaISs6!>%xNCkwTs-ga%e=sod?+!cc>9*BWB6RLY5SNTZQS)P`%|{Xl#$f_gU0_$QfiEd z0Q@ZA5nDW}?ed`f^kZ@~iwra7d&S}4r3W3v3OEK3C}Df$Qu%!Dm#&Q{1zmoj1CnLE z9x)#u|Hl1XooSe`E?Rth!aF5cM*FSR>shwb#~&eQ+)7GHpHfd+EdoqZ7%~1d@*mI% z4UJj`*8hZ!3eDqQ*Pi$@`T9@Lx1C$6jKU%ZE{(-=b?Yo7T|@aKJ3(R=_?cX^->=G=+f6%5 z;~1)DiyJ#3u0G>-rsQWb~vhLo)o(c`M_Z}p!4`L*po8!OGHmrWK5FlaT@hOSKoJx<`&%CtqNoq5_X{`fE| zJ%|fBJCkT=|LFS$yQGI_O~{hIrI%g57fXDwzj!yI1K%A^lDx7ldni9e>YQMRhKeX5 zqEAjwKXk73^ncnfu{1u(wdUELyvFz`EnM!}3sp`5`?SByb=Ay`GED_;8$lXx<}0iv z^wzGIv-QP`KSd#hmO;0+>V$Q?OGs&LGT_iEjLtbqR~5dUPqAm%Z8B6Z>ga4WQ3nR8 zUP)0#ldO&{I%?-_OVnFZLV#yUYOe7w%bs`71<2AUB7czm-3sD$IuH^1Oxhbo60>bI z&QV%XVe@dc|7m7sCR5wp*mbJ0p=4mPc>46v|Cq&W!$!GWfkEzkV081>)?DpA>xHs# z^r@||D*;qO>l!)g)l{hy!$GSf@AC0m8rWXQjJm;AhMGM#h`&i$!aW}mpK}rmNo}D! zQP}Z?Esg_m*;lW#Zc`(mSbsIag!nTaQm_9k%r3tK_P#n?fJk*Msns1|F z(j|YlTn1?vDsdT->2M0Nrc6Ux&vo-2Lys1lv%~}4Bb5vF{qj&{m}Oia>j@?lScg}8 z5fDP`lAm5a6)5Du0&#av?U3;kf5I-et!b=5pKRw+8wR7RwogX8;2-E}PRd+Te7*`k zfk`*OF*lU?J)27V^GZk8&P<_SZ=XK*#0Zl?qY8Ve8p$!7%fki&h!|1?(beZJRx5|K zwk9d{&f#@FS{sZrrzw1$*jfc19)_A7nl-I#=30Qoe>K>OszcPIwEGaavWEIP+A5na zsRuDZn7$Gbgav}$yW=@|KYm6 z|HJ&%_ZYjKCP2WYP#dJvtvZtWYN5(;^YAvZ0CPkac}T$q`7xWyY7r%%O-CX16d4#j zh{=xKeK;H%2|4>)Y=toesod2F)lx;;Yo+|B9=?P(J3o}ev-I@TTkROMw7x)}Hs72- zcMhl2|B@=)i6n0IciiXrd_+=RBfB4b&oa9v|E6!YdfWEoCs{15HX^@U3sZad;at5^ zo+C(mY4-Cvv~dF^%iAlZW-4*Jzl9Bz z-)be4cPCa-jFo9axX~=3{`Irrudv)lrGJ=sgBEG3Q|^9sq$bb~tKH<#IYBMAy4Qp_kuNv! z+n3kt;q4YJbR3|@a@XWKPjHG^oEB(|co@*}@Tj3JpF6Z1T~hg-!3l8{@&SE-4p-?m zsp%m91kil^y>CXtXo_Cz${M~uK8V*lOm?&pw8K7}Z$~-4Us5l7I8>g`qynsk)L7AJ z%Vd0Tk)8azql^Uxx%l(3oyT5h7RJxq9Qhq8mBB1sX6qbcdREbMS=;xYC zy_uoEe7elbGpf+*qIzK7<#p5m)Qigh4~f%!?mP?aJf~3Nae4corGt7aWWo?_XWhYq5OItQ6L@u1M;Y z;{ASc`}od3M>wqo4S%W4UNTZLn16r05K7&oZbjq%1s3+MAm?DQRQYP5sqG$>kT&kX zc{yU5r*vk9i_h~8JI+!u@-?JVA14rvB-Cth>GqO{Q(mHw{xy=cJ>TV}%=PSlX9m@D zkb&eir61cLwD#8z2Gb_paKyx&&oPcVj()_plFQ<;H? z_6)a{;0>+2#U?c0q3;b`(9dP27j<>-H`c#peoo9wPa(I`NYU1H^I905`XBg3$jB%g za+<4B@&qY~Kio5D{7lAzO*af7=z_61fG_MmAEX* z{Z&}1l2=w#vtfLm%^OwT$+QxQo?g|bjP z^+IJH$D@L>udD(i`)+ZoMgE1w0H*H;98Zh@Ze`k?4ePhS)o6h#d*_Q~1?vOXTQg>6EXu~4q zI@+&fh}uBvrBFWuxNm~pCFf|_-=yw-Jn^_tn{0cc@P$^mmxDT*I$BqkqRYLPo4{z; zM(J*AN3V|MYKuo`tI)2oRY7U-;Jy;z6aITwD#01aVsGW^G1bG@0VS#|+jR+6_-

    7E|!D-Sve zSRFF?SSdx5Y1O^HO-IJ&T_zE83Eww_B{8_(va$ru1Of z^L~+QD5D;o!UAuCqww4flr@vrWPV5~m;y4K@v*jocxE46N(Vcx$nPZOhMXLJkh$d?W^n zd;HVY5M02cp^|b5&dDEAlEdV69-&xV2OXQx>8f5S)tfVJ$76lk z1o*<*YA~=~A-nBE-%4wf-yd$^f3ps>txWCzYi{Q%K}3KJ4PdAL5&9BmU}UtrIrfvp ze==fl-EuK8lMjA@!GLZll`x)AiZyE#7j}Z~o@!-P94o~k>sf9)*2?ZUH)>bT#mFe2 z%^7y1$X#m{@a>yCK0(~4X{HiW&EwWyh*g@bhN3y957wwW5VLv!SO}WM-XI1Dg{>zh}3l{vJ&(x8@wwhMd2z(`kdfcE66!T#v}v0`Ug~khBCP39@(Gn5OT&7@+0q zc1J-8B?IK&%_;U7+Td2_Nw7z6EBC~2irlIc)#g(B5HGL%LV3( z>W%q^&xaTpU9OQrnEm}sX}07wp$e;Tas4NSytnqTKou4;+Vcrl3D^1SBThdXsG#Eh zeVJfbtf1*+>F4mex-UD^Lx}NIcMGh+!8!oZm~}cGBB}1Ygjb31u`#(6xUHh1#^0XEI58<{VETJZ3}7NL#sV@^9IdMFy@GR%tr-ji0#R2h3)jt)l|274 zeA=UyvA)_B^gso?FcH{%^j<$L=-8XXo8@>;wfE+CmPlDX1|D9J=|p}uP}aHIY0!Jz zo+TE7xp$)8Zp***z(N!+MT1X3&@n$x1h}8w9tm%l+1dT0qR`q1Sg^gkyqH;7gk4;? z0GG}`4yx8MIEeW1<3~Gzt*x!#@Nh8)2X?1C}2k!N;xK{bHjjjb8}O_*?v``)r#3C2a*iBB4&2o8-<65|2f_R zH7sz^IqA+MgQ=8q9R=!Fi}ERZ*#wTSxl>a$p}6pXu7(5lZV(i*5IOVKLAAx6((NNz z&o7%q1QPZz+_*(0VPWA0w?o>!slu>wIAk#Z^?(JcA!hf`N9*maf_Q58`^%Wez8C(j zz*QCx97m_t`-^PZMw&XClZg)uD{kPIe@v$Y9`k(P;F$Z$+%q`7*LuRZ-zvWx4h4{8 zE0IH&S_OR5(uf}(9yjj0|EAXpkYHetHUHcoN%UJ2yT}U+f{q1!7!=Rx%91j5jlby$ z(sLmsOxe9W%e@%u&Z{}VmgubYi4Uw+V!o0lf9 z0a^4GcdcU9`)QUbsuNRE1pyZqm&@ynOE!Z$Vs$zEThQ{+e7jnkYOAJWECU4h-

    s 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 Z^fM-PU}ew__$vj9`x?6H<*JV&{sTNLsWJcn diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd.png deleted file mode 100644 index 4ce6435418331ede88a4546e116148f0f721605d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25428 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 Or+eK{8*}yN$NvL};vR|s diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun.png deleted file mode 100644 index 6e5ea2afc63d0c887c4a23d183e1501615d84f02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25541 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 D?Oif4 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online.png deleted file mode 100644 index f66a7ee1bd55919be4c7139146ddbbf92ef82170..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23275 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png deleted file mode 100644 index 0b64a27ec73ef1c0119ca3e1c5a9696875786f70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22763 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeAction.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeAction.png deleted file mode 100644 index 1ffb0f987e34d2f43d625c377b2729647515496b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9223 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 lh~IrH|AqHvEHBKX)myk~h|_c@f9xns6_d7p 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 AuK)l5 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithTwoAction.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithTwoAction.png deleted file mode 100644 index e9fd77fa38138e6d34d7cd65a905ce2a41c8a10a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8637 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 WR_Iy2OR7R5Q&G}XESG=%{{H}dAAdr_A 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog.png deleted file mode 100644 index 237dcd156a4b766ec38ff7a34ac4e94a431ee06a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1988 zcma)-X*3(?8h|6UR7YCcgji}x1sT*(S}DpTQAvm;w897%V<)k;=w^DSv@{x$qShiQ zrX$8OCDAL|*ju%xYAe%Ps#GlzO3TgdnS1V>Gk@-n?|aVo<9W~b>wRvyIy-<4s2u!R7J`_ecxrabIOJG{Z3Nii0ow2s=6{Hgl8^}e z3t7^VH&(tC%80#BjaF<_g1((8bJZpSG5@pvZhy5TQ~tC8ENS{-09O7hQ$pN(>c>ug zo16Kv;395h$bsO=YfntslU@@=>r7SQ(`lko=-s~GI2GMB>|vH735cjhZ&Gb%A--U< zHLgZ7@}8edBfX;eL_21y|8xS~vQbx;uR=uO9~xK>V;s_JNX!_6EH6vtq;}%9AW+lngt6-VXlJAbHJD%8-BM6zI8G9 z>`?w@pX(w4Bl!(_Z0`6@)_&yE1vN^zwl1qdzBtX?dE5sb+oM@Ue^;l&?#;W{HZe?c zN1us6i8nVH_4OxMm$OmgJbRoYSxf@gy$e-3YPfr2?ZS+DM~`!^+p;GzK6KCjpFZ;@ zg%t$VlKwu2^se^ypsol`;yP$7FaK<grESaT$*zXvn#}a-niR(lA;% zYIIv>A^!B5NCXaqKxT&r2T>pp2&|!D)DgbNVUN}a+n&ej8M>oS1V2}tmpiyz@ko5K z=amS-YEYSTiAoHZ$Rb{_vjcFOn}^t`j^H!4g|f1;l}k>l#<#t(=Tv@CAif;-a8GRN zS>f`wPsbRL?}CpUMKknmADdK1RloUocMKoiGU3!noq90HV1xH!Kj&c=134?x`WBB` zi?)ZFny$mW6uEZ$JBYFI@v*UG!dnrQ(NwAGtlb&W!|K6w)8LU}GHjN=J7yLh9?AOP zH&6Uy`Cl{h^ZLG1`Jdoda%%4#-;48NQOS2Q^YhgZ6gLeR+orPWqdCp!kmk|Thc%=g z>MoG%J|r0%XZkt$m->6Gdx9ppacN`Y<5i7~xz~f`2EOcUR=s^`+#`%U#d@b+eF_s# zxNJ2r!;26`EbyBz+HYU|IH}8`es>w=t5Ev6CS(6_i1n);wY5V}tBJiPnyJGOm% z^=$r9LV0;vn9CDNIxVC0O*tIK3!V=RolTeaZiz%jJ?mkeqrwLbGyFXJvK5v#yynt`iEMd+T+*5-q>lAq!lp1-JpEC@A2*TMn%y+YbWf4*BduVR-dl^G2(l=OMy{Ppqu)T;m=q>^*%4m`VGN$L+_N7Hv3#y zA^Q)I-<*{a5)`+ujo|jG5En4XzM5U+N9BzXgb78c04_P>^KKtwUMC8TU-TakUpb=R zd?YLAZFPNGuXx@1pz|p5(AC?Lh7=sr`-owV^P`MEGmI;BblBtqtz{m%ao0FCQ9lIz}Y)v JIH-SI|2Hz4k)!|s diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png deleted file mode 100644 index d4dfd20689b3b382fc8dd0232e48191867e72332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7827 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 Hk6!;9AHk^~ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testOptionalPasswordDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testOptionalPasswordDialog.png deleted file mode 100644 index 69859a47cd51b830a3f0de0c2b00d7f8d9c06a05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9506 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

    *#L$#lrtx?fH|hzl8qoQqF%_@1Y;fpBIJx gE=>B`d%(6>wlqzQ+X<)dJ^{4U_0>ur{_*C200c5yGXMYp diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testProfileBottomSheet.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testProfileBottomSheet.png deleted file mode 100644 index 1c57c3c2d43435b01e3def8270c839c40ab26453..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14665 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 N**V)b{&@43{{ZM~Cp`cF diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png deleted file mode 100644 index 9a367f27563e6ac4af42cdb5eaafea6a21fbde48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4609 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 H+xPz);C&q4 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png deleted file mode 100644 index 5e73170338795c63e5a00ee9605dc00f12e3a3ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5083 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 N**V)b{&@43{{ZM~Cp`cF diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog.png deleted file mode 100644 index 18f8e02ce8aafee723723f1c474e63d5d021e92a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8644 zcmdsdXH=8h)@~3tYzrVDqEr74T)GtS zOIYgsYV^o(YPVC6Bjlm(vamJ%41}LW@Xt4IWJYw#Mv4q6_s?Ir&<>^Lyw!JQd-wKw z8+z$n^1!9!X2to?rI{Nc#{g003m8cN;1m}Cu>NnuD|SFs2mtUu2S67qKuZnqF9TqX z1#n&j@R#BLBU4d89@tFfOuxUs{Mc^YYyV^5dRDF1sy`j9LPMW6r8iC%A~i&el=hT< z?7Trs0a7G4Ker))cqlo(H3A-WPpx%6se*ks`$$Kof5?n_>--Pbt~?$9w$w~Ccu+^} z^N#9~j&qizc^N%>mVW{IAF%%m(Eot_FF-TV|BKK5(}RO=b8;N&{s@Bf4-#N#H9!t| z{VNu0HGk!P+*bSKNs&r$ZfF*B{`%%l=O0UX)k7bLc<~~9!j8*?b&MjxjZ{}g!WC8Q zZT-R^{{*ZnO{;GW-F6!(^etl~!druIQDfDFDDbJ@ivg_f@S>}B@8`jUD_(L4x^+Y` z_EuXd^1HFUxR$^QNz3v=gkvkI{F(KcVw-jyA8#lE%r$Lm?3N>4p^nMX#JQ*PIFqZyS{J zn-4ZurMCqA@|$r{N?qdAQtRtrG_eZo4(qkl*(+i)fqI1S{VGYT%F?Z^fGWFsJ0O@* z=)8FNVbTdQ&Iwv1%ezl}J8VuM8tGW6I>)|<<$KeUe>-+-O$_#vxtAJeMb~dy5QWOg zoM!(O%6`azYrX66Tt?6EmviU8M9MiySt05sClS;o*6!#RCjzHE4@aQlk5Z+q&EUAe zx-hB$2Cix>8oraSaqvB&-v6S-2zf3bZTuvDQDsw!-7%tG*yaQqoiIUy2NUY^Xe*<7 z2?C0eH#1P%IC9WAZ!~?|%r?>75w^d*f<%}2mlz2ELGfv_&WJ2|cN`f*$-Lq6CheMi zajrV#XPUjYrMC8m_K3k;^=*o(@7!eAa!pUbXoBMDu!EAl8J1Ui=dx5hvzV_I8(_6Y z>z?uw7Z=_3;?K40F52Jx)k+D7+0<-0_e~s1Euu3T@z80MA!;cX60juP!^BEqQ!}z! z3Na*x)iNnEbWsH)3esMP2q@e;6j8AtC{_E=>GTuynGfA@D-|Rf9%4=XK%P2>>Fw(7 z{*|aosbj@QI)&V#@6(X1eqnYkP7rz^L=H`f?M*(xF?P8|#t3X%VO{MNIU&8&5=b83 zd8IXwsk#IkT*645hEcJ!ESI*m=o2{rOX+o@1UBqu%pQTAV}u>+F{~plCf12XFzDZ7 z1mwf&zNi2bpV9ZpsC|n0lP85{W@cOK;(0%(zG^T%mgGwUhXBLTTb z&p(TOhC%F065(Tw-g%D^iihUt&29T2A7EQs+p^ST1)|05aZjSZlL12V=7Ze$shf}4 zVoOUUz)>Mq`S}+@rEup0*f?73H1F+xLhaE*C!_<>TO-<t{1=TlL44Wz*$R&{kKR!q;W^JN0A)}`XnBNE>)4PzI_9Bge+Yh89i&%2_#C%PYR z9A263Im^%Q&HgLfyJZ~~NNOurl=QY+ML3#+M-3!BNjwcyRRNu}1)YL+xH>QY4wW}g`^L(|+p64B%X`{U zPe+S;gg;^qyY7UChYy40%uA+VtZXMGcJdDOt(B@JFo2IC!*h?>q82gsr+{p?_{3aEqP z8@vhmcJ*$!X?__8SIsOsS5eLHjnuo3YpgPo_c#RfjnrD4Mu-`<(Z_5(+RqBYoE zp84n#GA56-KP2?D?QX(DPMUx~#dIem`u)RBwxU|0q8btULHLO|e>It4JmZXiw7acF zFksHt#?`fS_KB(0_A=g6GmB;I$$+WiKuo6_O&h+Q~YzIj9a+gy-lREU(Qp`6eUKKiFE&E!0~(Thdn3&$JLUBKDvD z8Yv=r*{TMwdd$~6>E+QKuW}FeBKVEoSCC$OlxVWI(O>Oxb71je!Q&F6q6Q(Cc9H(M z-;v?>#iK-#HK7p(%aT{EwuqcOykgu?{RU&|yqFV>bg`XQLUKIbuAC}2EqF;k_#7uA z-PGQH*>QPT_rw|LvCq1p`>J-0Zp0dLjdZRmU|j;N67zDb{SEU#?oM0~N%qz9W^rgnu+vu1WqY#M4y~^A$Qtu;r z;vEh_9PF{iuFjOQa*!&09;OKzv{7Ikl&X#&N>IcXGA~tpDDj{`rfu!wK0S{A+DP56 z(G}aydN;m9&nsch!*>fb_P%8?9iJLV$p@W4Co(P3hP=QIhm zzdBw~jVd{l(^sOb?=fAqkkLRS3Vkf1jz-yj(^+7`OGLR}oM50wvPrZedzvCjh+ zstfcIYQa+3&xo6YJB^g^+j?l)+lQdD!Gyqc z1bSPgReeTi zj)5OOeE5g=k9D82pqeFZX3R#tbwaucy6}za*V-Pi5_LBBGSg-vrG0Rs z>@m6T%}JVz3zHs7WGyCK%!#TTk7>2G=5H z+sQ!MRKdws)tVBbqUm03H>?PQ-|{ij$rf^-jk#ur@A2ipzP!Z5-kj^S#MLY6!lx~U zS0?3C(0SRK%mT!>7~^=*`UQ&AwGX~>RrxZxkf2H(UF~US>^-XyPzg^;tBg0VC{B9e zb=MF(0<1nLKZI1m%dI)a%*E1vWtUBFH9DDYZ?mrdB8^uythN@t{u17707JJ~P)oFZ7=34pie1P_>1p36?!!uE)$tBhvf|#O@A0NDpoF5B;PD#LS(k9%OkehABfE5?^1h$wbxFkK7qVJ$8pdB&@qx_tMahLrWo5sm^fn)T zZPX|syRK-63NjzVB^(P|Ur9AI!kB44dWloeWQgL4M2CS$&P_iJ3>J&Y&KJ5jDX;|H zn^ozA!pRQ80(0gIQ`}L=$q9I`0JWP9WB1WI@7|1#S|4tE)?j-+ko66JVtB94d}tZ=2IT)Qc6R&gVKvz)b~xjWt%lyhVK zt7s#W5UQl4G{OpuFz=K?b^4*5-YAG7xwF^C z#!VV4uk{M`LAY6QSj0}G)_X3Mq5ZC_4&;WuPsN|5XXiqR*1D6NHB0Htv#ivgLFd(@ zJiflKgg-Q15<}_jO$KtT5JRdjX+Y>7qN_2B2yWv#shz#KM9Y=S&K?1-`O_?aub3lR zh3>|thX@A!SJC&+XarkW?RgPF-FaJ(8HTA}Giyl0@3jcQ#yI-)?X1=UgZ4z4+hT}B zPMIc|(mnz_I>kY)7Px&C=u&m?S>c=2!ZQbTz|!C#MC->fDA;IG zqo_MVmhe`=sE}Fb&;3~Ut2Kw)cDjiJe6c4lYok8hClWVg)v|p-vbJS{soB{V8ol{; zk17O!&dNaS_wrr(jHcZ*2LnKAi$Rnq2`g!)jj~$cd}`hPZSgbDHnyv)ww^0vT^gb* z4htUS>E9XgD>`7j2#?O5E-hTn{vPg~5?$`Fa??JPJ(Vw-WDRrBcV73|y=tKB2t+0F0M?=$@g--_QysyG-fTO0>js$%b zf1h~dqO3V+!|ifORWPa~mRER=>4T8fN-^@=TC+%h(5XqB&sorS8mCA3z!>$9b)V*# z6nITP)@(+D8(UeB_W48ZjmD2WET>@p_q~EX&@VO(H*<0UTT=G+;4xnp(X5Qsc&?fZl4Ac_Vf}!_-HTX_Dtb`GtT$Ysbj2q$Qmr#F* z&~}@VNe`B&;AVO-y{Szsm8^47TnaDp(DJ%iFGli}o{`l%-Z!=z9G+mYIl-BG_-7m` zA9t_16nmvP>u+{yCifrzgP9p#hcwYwnnjOeg@pm0((^6AQjnv^!<}O+m zq}&s|agS|*cjQa%GPvM7vFd$!3>RfHt{LiE1x{&=l-=(}2QA$_XD zkZ#iqc-1o{twtK=#e=Y;f4*8K=`36%J`wLH4oJ5-pkAPQg!%*>R@lH+?;grJt#(B7 z9z6k)lM!8lc=@f0^=1wqtsVJ|^|Q=}P3@gV40R)mM9#ZE_I;FTpEv{lEgVd56hm=v zFw6guST(=({3wKMVwTphs&56)Ic8`p;TY3FJ{|FYv7N+B-_8E;{2V2&6MN*?@Td6or!D5 z)J1VU!@Gl~cR+|6NWWA#yh3{E5kl76K?d9r03kZIwJ(t8FGp&ox95fcw5|f5DW&8T zScnT`8r{Se54kKX;D&^IbuM)fioR>5&luS-&8GO=GG29CFyS~!`XGO*rUvZO?a21viyy%Az$kwHVt%2 z8{5BFh59tm<&tuN{%OU{7d!H{1Y6vwd4TQk**sIi)U(96ET7a`V;(8KpyQYTJi&UZ+SHG&h4v z8rHj;nl?H(I`>|+6_AIRML((Zk3 z3)B8ewzj{+r(G6jB&e9x_|)K3DJS{^1~ehBQ?EPEu!QihSQ=QFgZECH3>^ zytn!F**q2nhVOBWl@>p%t}#hpsU?|Ha)>aEy(npYFmJgxA>3 zGIB-*Pcl`Q{JXL%UYZmJ6X^ADBwQYb$>Gt+HNb>gRrjeV==tn-=KMz7du^Q zdM~4RaLO|hWq3K#1+@5cMb4eNQQ$dV2n{KdjWjwY?8+w~;8yT9749-G=RPqQ=KAea z_gI`|SXh=**wZF_*rvL?>yXJY_IddQ07D^Tgu)>@B9^+RF%ss{{u89J;W-SwkgDNr{c3POPR?*#~qJ+o}E0yXCgX zG~TzmZn(NkdXn;$or_Nw+h!F+u3UP2#bb!*=uTSA z7MKp1S)9=z#c>D(fr0G`ehD%p6Wz{LS5WX<0<}w4K-nwCDXj2*Gd%RtoP1#)4CV$YxS*1SsTF#8{PJks={{ zl9LfU8_`;9oDG^!`Z+_p1-`Sfz&l8Kceh0X{~F_bh`SF28%+jI&f2VPVzTS&Xn8-5 zE~MF4PjZB)u3gtvu24}Mvo^+0YWG`p zKb=eQ+D9RrkJG(3f(`E* z)SJGEQ#JW1r=zDkjd1{hOl4KRU?uJ;#b?R8;b5_xmsQBh#6nn4U?TnTBGc8cG1oq6 zgd*b#v>zyldg9UwUtM<=gynbgyYC~O1gfI5w6(+IPUjwc(!8onQ7_nD zHL-b`#!}YOvOAf@_p#=7b~pp=T$<9zC~#{b8Rpvgg-V*5KMszFJ)^wU0|{IM!^97L z`2!=kxd7#kVm1%+HL{Q)d2F<}t2;fBX)4J0*h8cajeyg!l={Q9+j~81Yjv^ZV+0>$ zU)noVJ=Rn7+5MgzPR9x!Kl!Ic$df|QK!&7uV}cG@J|#uHhprDs2oVPoj*x+vzXawnR&dm*hu3#)^(d9`3(eg?#tgSHG6k zI2J+UygEYf{_=&yl)h__2{28ODpUmfH?a~0Z+v)`?5VamYDl6JGiP>j0Av+A*_1+ew}1JCKv3@k6HuL%}XgTDHKZpMIKq zv5Ixwwlcmz=Ov{7_7!0!s=X(U`&;C$I|WA_gdGxH z&VVVprjt`{b2#%OWpb?GLSWQh*ph_IkD000UO|c3At5)I%i3~&!QQ;E1jZjzvhOWZ z774wWf2>H^=KQ5^E8O0izZvKWkd}P)*G_G})QpsQIdDO?>rwV*KGM+>>`tI=AW*p%2#YovqFeh~d1D4L2q diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testSslUntrustedCertDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testSslUntrustedCertDialog.png deleted file mode 100644 index 655039e83e9b989914a09b8e88a32d4b7d02e3aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13576 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testStoragePermissionDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testStoragePermissionDialog.png deleted file mode 100644 index c0b19716c23e01b1762ca4ce46eeeced2b11874c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18812 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 Dvu)Gj diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialogDifferentTypes_Screenshot.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialogDifferentTypes_Screenshot.png deleted file mode 100644 index f8d775768078d9e098acaf16c07379385a4c3632..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21339 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

    g)YiR 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_Screenshot.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_Screenshot.png deleted file mode 100644 index f8d775768078d9e098acaf16c07379385a4c3632..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21339 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

    g)YiR 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png deleted file mode 100644 index 188f61ca86552096094610110a2e6ceae82bd027..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24764 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 i$>INp5v<4-J-`R0boG{+#eV=*0r>L( diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic.png deleted file mode 100644 index 10c94a3ae25a8e4e72ad1287b71699bb9e4d78b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21829 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

  1. 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 Qf4Pd|DVLKa$FC;*8!^{wYXATM diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showLoading.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showLoading.png deleted file mode 100644 index eadc8cdc3799d47b80784feaeabe50f02a184bcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9341 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png deleted file mode 100644 index 21818ebab472d4a250f6b28036b167adf9d0099c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41653 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 A!T#u2q*{$p(u!y0Fh1zAv%hv2#g{iH6k4( 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 E3mY2ObpQYW diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showGallery.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showGallery.png deleted file mode 100644 index 79989311e27c7984020fa7609a582e8ddc683480..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43125 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 p4Tpb7=D*?a*UbMT8+veRS-b| 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 R_=^j0aYP=f`0Pyb{{XrFas~hZ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png deleted file mode 100644 index b9124114040cde738100cdb98b6f4ca4f0b7ad1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14664 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 WUOnkc27e2N@1mjExze-0-v1x^h-EJT diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png deleted file mode 100644 index 5579f8b847f4243d718517cf2f390ddc6fd6cdfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39848 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
    B=?#q#OUxX0-~90%G2GuVZ3Bh;!w_ zCHoQ?#|1a&iM16CNA;|x@rcc(B)67ycxh7K@uSG;t)l^k^2^(=Nw&@;2zUO--+w=&Z#kvYbx za?Ix3n`=!o6kcg}^^y(1zT6Q<@*4en9&d}TBl|UAD^9fgO=n4~lxFSTSc|7X{X)HH z8`JdOY8~LJiorrL)vfCv*e*WO3iD{(0y#uP5XG}sZNuOMd`|%}kRcNeHLjPmeZONm zG@2%rOZa5NJ`Y*V27#qFQy>P|*p%Dvj;aJ84kg73n#ivHu5hXgZ9-V<>l&SMB#abqmLU|@B@h555s5GN# zvM?bm#D{zl4=om2CzO&T%&Rs*n%b5@HsW@dK}Jm80OW-Zf6`knen@3Vni=)M=a!Zg z=}c4!o8Q=r6I7!I^mj!ThfaEI;@L#I?Vv=*-BGZ_Xs5OWdGR|^4)0|p?~VF*o2$0X zlqa*Y7}B>kxT5rZOoq5CvQ;YUH0|C=o!~Fa`D+W^)1xA*quBEDHA|e{%IVEz@;&)9 zqz2LGG&FUzJS;N_axWkZmZ6iQ_RZGbz;-4sl<72d=mT@ww1Jo~I*#|W(}Qtue%qiz zxyp9rLA}93heP&hY3z)Q6tL<`P$PU zsv)+|Sz!VyKOuRTFM_V}iJ>aD9QyP8^j~;ez@{9N$R%m&2n=`!xKqu*r%CIxrPuN$ z((<72U}eH%2;$O2Bk&sMU^c8}dB2%k`dM;3_5o$LPty&!#dFOc-f^o*ngaz$RJ)C! z&;7J$hFwuo^2jOwK+paPD;@5qyXe=k#rNT`FZyprz9o*d_oWhiPVw&6&=N$N1IF&G zwuJiY5Tjii~!r)RFP zaMKFb*HwlY*gaWY7V&e$|Da$DOL1x*{N zHY0fLmaAZ}PqF%GPvh8x09N^-jIt8>SA7R&XnZWcm_fAJt&9rZ&b?>f7w_}rM*P>P zL!@no$MKWystJ}u2Er}2mpLrBXkr7p91=AGhQbM`PN$0wPxsv~J;RS-d6e3oHS{THLH2o%wS7NNOL-G9oRhcyn~ka17gz_0u^1 zArX`xhx!GMy0h-dVyscx_^=V`fF|)ZP2`KsVMyO~#A;W#6h$(8Ak9NGa`~Wbb5<)72bl2lQkAEDFp0t%cC;SEFJF+k z_Ob6%vTLfV>rsy=Brno;pe71&oF0G20Q9#C_0EZSf@CSRFe`6S;@?Q6leR@BcIgP7 z%9l(vltEl-la(-*+F{HX*5q@veGlfye<&o%pIL+t2EqNM14S}#r{Yk*bE!9DH^np& z=AN)W>)Tf_@nU6oZsWPP2>6ucNeK%l^GdM(o1Q&)?r3j}@efPqy&lMbIW3!RH!LVY ze$!E48AF)X@-KT7lHI>3>3~X+-62*}e|KX)HS&4bMwh>=JsE4=7g|83r5yGe&2UD% zKzUXSnLshX_h8Pso71@zOFGkRwFbd+#_=w=4jo(KgiyTDXcsTM6vdCv3J-WyKG(HA zu}It*9F}&B_5h!EJQY#Vn~Y$pqcS@iWYl$+XJC8ur!n>qqm@w&a5c@(O>p8YTnOuk zp2L;{t$>akS8^>y%h8s-LO4zK?QrZ0Lf9cpmPXxFjrYgg^UZ+S&?`2^)w- zrp)GzN?Q}T?nTeec++Y%Zr6Y{r296^2u&(aGVQ|GvCz-0nCmxiEu(ic%>z(R{}GYj zcK(g}_fD1K3ky9*y5us+^lW(&@>Gp{$b$>PO!R9L@cKR;FY2N>$E<$FbY^YY{PWvt zw2YuV;i0!0z4&cM!N6R^D1kle-|G^rScj}%3HX=X zjS1I8m|`tmbvM5GCegAWv7%R6wJoc4-||cxBh*IJ)UA8xwIJWWYz@W7UVmLAI76nQ zGlQVx&t=Q!F&9fP5gF2o)>Fpx#nKC})AULo5#}zNm*x_C6YyqIg^RZT)43f)jwx;S z@v4aw9ce)4JZCR2ksMeT1A_%^nxfF*eXIP?XGwcRx~a#oSnNV1YSPoe?NT1c%$9rL z(2j@rQ;Rqm3#V&#%XK}4PzbfTLM)8qm`1dr)fsy;K^IX!uE6ZtgvYq8`L15`a6@Bx zT?*o{Lx zl<{=kI|9ZtI|R^;&eue_Go~AdIyW*Fze(uGO7?>Ib%9S-G+WaS;S;FP1AEpG8YdG> z%`Z7&3h--{8|-c{?iRHx6rsIe33hhtB_pm$>-6gehFJrYXG{{?nYE#oBxz*E8BZN) zt$6sl-ch#*Z$wA$@v*sRt%XxrsM93$Q}o8?z}!JoqvGdrCkowf8Zc3Fy{6^;^Ol?! zOI1s34OHeZsc9lNn?*3qE*3%UeO~l;nU^O`y%k1bK*1GLrtxLtnhT7tjx($|(W=eL z%gZQP$9%j_D!R_~dGuiFnEzD~1^5i{;o=;lP?PYl0pd!?Aq=EE_8YMkt(x$tyhCJ- z926+gQ83sg8)lWIb(_j6dW5GF^(nH*CzwQ93m}^^7Iut7-Cfy<%i*iWMsZi+k!eGW*51WjY(#(C zDS9B0rL6eQj~qCGuG|By;T)nX%bG(uYG~8xVaWs#nCo1v^zAHn7j#-#-F8 zoV5W&z^q02o4-1@^O7c5$rHMvzG4gB~RiflECn6R?KXkd>tXt)a(41-UxXF|~-^-#FNdpnAd7rhP zcdxbgxAqv{xBrmgxbC`g&hz*k$2mjaC`e(T5~DtN@Bl+bTKw&U2MVppwA?N0a2QfPnrzts0Z){@A z?vrbW0{Z&W z|9JJk_}E{s{udwn$Cv)w$Nu@HPZ33yop;-I5MaNF|Bqk$=lA{Ro&4AL{pX$h>tp}t z$0mq7p8dKa-TS&Pj!856C*o9z?(+Gp3B|m_h@eBZGPli=<+3-l==QjW;y}=9+2gPY zZQr4r$V6khSi49nl3erWW=@)WzhpSEkxlcJap%6S=W!6H*;uFZqLcdVBr=t_431-` z=gsAD*VrpWbghE7S+bf8^cBWK_M$FRnkJe-s81G593aD058)7JG9|(+?{6<^sw}4G z;IIVrQu&<9=dBxX*_OLA)&iZTdZ^{*VB#?DvPDVL-h#u(P#{iMpPE8PeD2o0yHyvHi4%O&>&1Encr3xwH_3cX`GVJ5 z-_Q3dMn38`a^pl3b==a8P-Csz%69L+IZw2=Hg+usiW8R>4}0~ z=s?N)n&Ioy?e$KFWv`2>cwO&V$n^xf5R-_DxKlt6Pyg&s7F zY!Dnp!=7k(L1s$55#jqrJbn1h6o=t5yY=p%PRG#F?e)loRjmps=XV&E7(6avo_mtuVIXAaxI3@L_FJ-#V-PKf+hYR#SL`cD_CO zE#O2JJnBlX29H0wyB1Ua76_)@$|8mLxV=6*Frg^vd4%Er0tO|Lhb*p|5->oXkv)N802qru?1X~I!gjaCA#J$ECyIQ$5wB}~K{Vygh06Cqv$4!~nlsK@dUX8r$R<5LS!c_Ini-Eqw#K1G z@lrVh$Nl|n%RQv?D*gV(L|z2T-&EJ<_RRW?$XIG*Pr7~oGqy;YI?NFDV1}8W)(bb- zq&z>+d=w5A(cI~aX#$@v>V*air&kcQSN^hm*sidp=C32K*Qak3vnBB;C78*H@`?$! z^JAzB-go++%+g@c(sFjme8H2zX7EvN@I+rBC6&jK>4=(g;RG_m`fII6djygKM<76S zTstdh_>Ccqg48u(I3xiNzjZ@_kn)D_c#bs?`Gt1=5TYM5FBy1{o>SCDk=Zm%P7 zS)z~$c{=;92m%HeT5+UG-c{w}IT&9V(x_|M1yzjsK`P*z5=1_i8r#%w({tCi*VEA} zI5!}*8Dny%NB#63V6Nd|qu{mw;ot^!!<;?zf^?XE{73~BtGeK>Z#YB*T_Kf#!FVR*xr^>&nIlCcGh z5NJ&dh@W5R59zP6Q!Ms$32I~x!ZC2QPn2aiDcU7&48-$OozF*G!NDP(uM2#ZcKey_ z3Rz78!1QFF0y|kT1a}W**^YeV9D<k@3}c^nKyfJbAC| zbqlL$PRkAk0th)wL#usCA8CI9=V+iS>5;SRr3$8XV$yxrj;%6Hv+TR$5y%{kB8W^Sixidp?IF?XQd3&N*H26? zEu;*LaiM#G34#)KwhmWro3Gjuq`w7H4r06RS4}UdG^-Y<)%AP)!UF?NybjW71jkApHEFT>+jy>N1Sy^`% zv4v_io}vKT%cjsg9m7C_E)=T3r(TyqS<;_qP*@Tk2NSMEk4*lkO&PW~QDxEEEk%lVzIv8+cF5IG?*$&@LPP{HqSx;(eD1j+>pQ$uTc5?BQa>`} zu6QN$W&+0@wJK07n@6<76;^wJ0XvzPis6Jzj*OA~8v`8qHzcj!OZg*Q_Ur?xX7AcW zX?1?|L{d=9kr%}A{`Hv0c&3Q3X@7szfn{A5EA7yc zcPDAYM?oPweRsOfknX9^s9x;IIeGhTSH&Vs<$ap0^+c-TkH|pm%-XiMCg%U%GEl5xbNVOVF&KBZp zcb?XEsmnJ}k(NW;iLV$%3niC-7)Cp4=yH8EyFHh_W2Is&_Bn4>LFS!#(K^aEY55nC z4ULSgb=+N_Z)F_~&`sQOYGJGDz4!UK}_^aH81+)PJ~F?p_haa?eSEjrv&Ox9+& z7sw>yaJw#3g__nyCr3EnT!v!c3HzAC-*oGPf+VOp1RN3<(m)k^dWsMRlH6SYmQbnp z?e&wqKWFl&*h%+8go%DNeb*BL34BNR__4IQM|LLWDZzXho>yOTCH#LO>?9fBKqn{t zS3Y&qVUY1TA~P2yJw->RS4c61LqwN`-XZf?J9j47_D99;z84b|Ym4g&ebnBA&D&Z7 z$$99V8=5r&cu)^)yX;&X;;r!<#cx&y-Ji=8Qu(s7{(McB#QS zV?MoiS|e$KR$HUrzK!{TiRDxMeXAc_Y5@_O!PyIQl$E(WsFBY45-w*JMRqLW4MYd#<7;$BPt-_(Hh zul}!#&Ht6A6vs!qt~49p1Dc|oPA<-{((i0{y2k5#w&xrOrNZ4tWd+_Y1>sxA-Jjp3 zCcKK9ugxtca-$YKPlgUa{pMp6`&_lW>3ojyn}vKLd)HL4w#EK@O=P`dIV6x{n5W|o z;&a!-(sVi&cz(YY###t2ZuM?)!%m4Lm}Wj@&}^>vM6$*qvg@t9LspZ)jv>%-Z8M)P zwyIh7_7H`(oK3wy2f}8KV?-dP+9A80CQ?AZht%Qo+0LY#4=1>iZ{cryD14aCKYE`G zanp&89=6qP}n`HR`gC&M<>ne z_H0_M+0%{xcQ1`q&7w2fJ_pcq+`guZ^-fzAD0uWU)mHO+qheT#*1jBOW3(=5G!Z=Q z&frnVo!%skWmISRC92!#TIL1n@?za&E-UjMa*smQqIaCDP!wUvOqs!A)7Sr5Ooq(1 z)K1PYIeAz+-_ZRy7^s|`RrgKN727@+hd}%K7n=d zXRkkz@HytI5@BvF*EkaB1w{ee&!BJF_InR8c5>fnt6?*Sr!~CwY z^!9vy{!b1ko7KzpC-I5yaCD#rjJaDS)-9*5{9hDI;3G}Zu&_*RtcFUR4 zw(Il#U2#;l*N99C@Q>}pa<4BA4+AkC${S8dTh@yOV>$zw$zQ{(D=+?~mWMj|IqjGr zkU?tSB{8Op9fo;(y2D9oQZq_)n&zx)R}rVZ=UQ8~cf_tg0F~k5WB7zo4Sm1D^e44o zcp&z(_rIKWChSIJ(;U)#Zq_Nzf5j-y0TsP?BI&R5$#lND2#>n8UtqVe!m`!(;qW`U zHYTMAzMb4J$o9=ZKl4&`^A)R?=nNXnLQY16T`~_0Ryy;D9zW(R{F$fBz_8h@J&1b= zUP0RrXpQ!2v0mh3=$tH2SA0)lFC6%Y+;zool!5^I!nJK2K%l;I`wiK7vz*kF1St+f z*Ms7BRkHp%NmR}lozXEkr?$3U(34I)N1MEB1A(Z8HHj>(I{Rucg^8wv=SxmuB9i}r zr$Dp9_#O1iuE=_K_}K=o=wqVaPVHSO7tHUgE5S>%{s?HI_%}||+9ojNKUL`kboj6V zDuK46(ziFo@PcrzWpdpcS`#OLOLX0a1vS#>wYCx5cazvuPV4$C4g)Asx!6E(dL#no z3R*OLZcZ70{Ro#YjdbE(=X@**eB>E!x<@1~4lP;@Td_=rJ5Z#cBRc?UEd>XRU<0MdA<8zeRUr%=WV zOosuxn3z&Y`}gc!X8LDTEG`G(YhtvWZb!(V;AGd2IL2YwY`bXKD-RpkK32P)DtceB zL+&q0LLMluWR5@<_SMo-iz4KOj3p$^qZrR%20S-b6S+#(s6=bSl|=w>%+%!sd?w`` zdwmARo>V;JX!cS^yt2`{sV)NDz384^c$0ntqHWrwsN78|7IC(8Tb}G(a)gXIvdK+r zn2K?y{TaD!ebZ)ppLf2BK%ljP`^%Jp%~NumwdKZs;SD%d52l=JlLfT~Kiu714a;8q zSg%V^D6U;YHOYb-@kDV?Y7-2Yc@az;A$)f!O14;GqR5?v?nnJTb$_q|6Vi^xTIq9l zlCFUMunjUJ{=uA-4Kl#mvS9Q*rgthwk%E8wM~o=+#Ty>*7}W6N+kNJ&5m2fXE_6Kb z0g!{scI6fKlSn^tCXvYL;Dz~xItQ`YYu5+T)Z#Nhagb{)ij}_6a*JgvAaYWoI`>kGM9zbg`L`nGTDF^&pp>d8ML~&zBftJ2yJF~ghZ|w zV=Cs-1PD%fl+TrQm(=e(pT^(xM=EHJ3Od+rm7=*x0c>#78M**2D%-^e%C^?dfLwwq ztZk0|pSU?D;^9ULsttfSZOhOFhV+4vSnVki(g+2Wg7MJGTZY0=2rdm3^2nPsxvZ$S z>5mC)^3^hl4ny(%LM1CjIwGifa$kKWDit3HjGPLd_{N~)xRaMtzW~QB2ymq!w=0fl zh_s$K7>{g*;kF@(F*c`w0`zQLUS9GXxS|>lC2}x0GK~-kB+Y&V@<`410Uik?a_@^6 zrp%5;b7jqfeVE|g<@zw(E20@N6ODK6f%8VXnHV#qU{|E4PqHM;WRLqXQs~;0pkHiH z*JGbk9UAaFA%u6S-7+j)EU(x8V!-aLSR>E38JSuS4e^h%Hq%-T;RW;LpOxvxf>ByCgCw0^tdxfvBCh=q>WGho^S zFjHHLO!TSc{?4OJn}2MLhMK*zfu^ewZ(l50gZ+=vVq&|P-JLMak$*XEh9kygpRZdU zqFRbd`e@jDkat9lEf^>%sY1K5?&rGh;EI0C=4K;El0O`KSF8x8XAasK#GK64p%V)+ z*Ij%?U0W~o@>$LA?W}~cL%$s3_zUb;WhvP70~xz<&G-+TvDzc5h8vB+K$eB1_S&G| zTKa2$yo*G>Z&p?wN;~wZt5|xBPD;w~%_vdm8*%tvYj41#&_ek{M_8Yd;!uSMhl zS~p{Nv#Wb=&t`8rn&HRz_6cot0-kOE-dd*t}hd6x=t~ z1DJpdA(MKMoce=X*Lxn1Ws|mb^&=K*N9xeO=`oY9K{L==x0j!2(iAqR`fKgU)OL6; zR|0CFV_!l;T-toT^`cO!;oTX@Jzf(NzBvjg@a%m0l9Zjtj{nNA!f{_wI9ghC3KcFhOJ`jigSo#u7 zp9_sL*}f9ErkftnKe8;3;*TujR$O$Gon1qrx_dlm@TztDWH^*zPXC~F>o>Wq~ZGb*G;P?~;yyRcgH(EA1ENlY&;>jfT$ zEz;e{U>FQO<8=^-*@vBoSlCor>>7(SkfL6jKy&Hp?+}&JXV1`wfhMqM_&hBlYsj~) zN4M12PA;aFV0WNBJ`~3Qa78pPa>svDZPqGD2e)PprCN+9eI5628tLxakSH`-yl+2E zfFq4UC4BaEV-a(y1z>{t+v0=Qz|U4@eznUr4lvy`z=ZIZ_P?5jH48 zYI_ncN6hEAr58@Zt&lJHN4agO@ylKOE!>dhDkS|U$4Y;DdA}<6q7B5Bcqry)!X$}c z@k)>ISU$Tw+FDZg%p5<<;<$|*vb;sCz_==Tojk;Ke`Zgu*1V!hb+4Kitr^>I zY&0MXb238;agG~RjV-2=tQH=7wS<$P>4-d6ds*3mJTKy8?#m?@6z#*>1i)HCMNuEVH1BUj3=1KX}2?0>|ft4X<; z$S2(1%z3};?ljo{H7bKZf3DK(d|Xj@HkwY3eXjNTYUF=!Gpa`HlOR{-DP`rJL`J^t_; z9FOUf=PIMhZ$fa}$2PhcH${{nb7#Azdwt~Llv_KSgo_O9B}Op4U5(|N63 z2>3p3cTbByJMGQ<)NB^PXVKcKW_@2h2uRRoXO(R1^KbiLDyZ5VN^aN>WB^sXrAbsV z5J+xhV3P?L4y)HA_=~?tz{Kh0w2NfSGnUv4iGB7P2r}I?{;!Z3b4;C~PRj8b?Yg;&;a44Q$ z$Nt_AAm)^<%Yg79?}JYmGo;vx-mzAETm)66^NJtRT+_vp#l~II{1HGuvkiyM7Qy81 zxvx|CCds`{l{|o}ZFSgmff=dE`zUmwqR*i**D|u@|^4c)2e@;bHKT>Xa!r&l6G04XORz&$z!|vs?|R;|vNn6oZs!^vo>t_GG7^q&~qd)Bo;t zTyg$GFd06s<1$w}o98)r1ZE8)1&up`v0UKLGlW^2PTyMm%naHcfmxeeHcNXt!0LBK zH>@Jgkq=~UPCG6L2Wsy5oHWN%}Lw#%Z^u(=QLhf*c#jE9I# z>RNzEKOhcau_9zQ4Efu^48*YzB0|I<{-wW?g7kCU`#)^dT6-%EfJPePDK*+OPRKjI zv|$FI7HZ86JopvPy<{6F{NmMG6TzuCG$(g7zA_v-Tlyj3Y=3?lMGy|@)ygsH<=rV% zTn_QEs3AG~+&Cv^(bLh7Pp+);)J3%pfjc8Z-LcdCBz&L&7-I^i{Rkh;LKMPVJlR4J zG|zzzYGTUoj}GPtCl=j60vBgnidKZZu9P=jCh1*hgG?(MTc=@KXMNR(yys-q}TQ=Ck6MUmfdG4 zO78_unqKg$HT>X}R;nkS^gPGcly_dWggMD7PzJKgW`hT-2u*+Xgd)_i>Z0CRx_P6q zx_4Ut)r*yChP`nmtS&ce!}By0hiFoCWd3i8fEi>c%ZPq=MQ>Nox&S-k!eShq593W2 zq_qyujzq^22^b5!*FjroPuyKl&6*;83{RvdrDS z$t-Ohr##+$wmC-LAJ9EnjZTmCl~06$MS+w=scAFGLJOZqxnS#; zErHWZ>^y)o_y)+2#Z^iVLZfB-(3lo2-KTW$6 zg+b0liiZWiA1%jX$6_-l#dQmYmjnF^bVGj@Q}(TAj~`=B%Q=5~uLNZv^;Sf8_76z6 zm2Rf_PDX(5B|$HM;@a8oCvTxc22`SZ4ui{?fIO=qDQs?&hGv10oIs$ z#X$6e%j{%p`{{4tCTVWj+8|RAbZ$`WRr)48nCL-XTr;!mJ^ZoqxjqWtI4X{1G>V^R z$O;Aet<3PNQsLun%02MF7>U^rOPYLG9=rE!;JlyQ|4LJMU!*uQLF7PvFrTgJ&~Sek zKd)2dVyDJid~P^X`^@1Yyv+_~;5Pcm8KI|cYYj3=ZMl9>LWH;(XnyR_I-``=HE4l( zwijHi;*8QiPAIK`Q(K287ttlQ!tt}oDIA-<3U&8o8XZv)JnON+TtHVO0WjoW4V{`g zjSGURU16d5LH5G2Y_?BACc&=Y$=tdhl5ZOhKq`+C=m`&s^hzbEGY=J?Ph7!^P>CPo z;8TSS%p5P*!}Bs(GadT0IL8Ze*jL`JAJ>RlUlC6hyjx~es7YHy%N{JhUHxD~m-;5& za=tw3ZPQ#5L6QnpU-&cIJa^QDLoy6_9}00L3tK&B9v#e3Y;Vf}kjtP$#fm ztVJ#;Cm7Pb!thQl+|#W)g0%>6Z~QJI9pkPd%-;*hO2jqjqQ)4p1%)7%x!@A^4PTrL z`uVf1vYgxY{`LXPSc@$c<`>(dp?JWC#KZ=P*S{redm`?;yQV(qHJ_zP(?cVEz+7b1 zY9QUU zHtU#oF>s+_EJ9rXfJZJ1znzJ4W;lpG@QJZ2=8|bg()FgxT-O?uCVXQO!Q=76%U*i@vPLdd+V57Iu}YP z$cIIc$bMy$AWV-$NZNId=b0&3*<_0;>3rp^?@)ha(DkC4%_Na{4|yI;{Tt6Clv0_9 za><58pM7OBx>lw2GcK-5CrK^7%}bF~ec72%UQHtv;pjAc9QY=KE}_TQcO1f`7Gny` zIq`CpJW}1!V5~YWX2xQ+7a63M0{Lovdx8*^I1lLgK3b?4?V1%qaq2B(79&mvgtg%(kJ@lQMYt-3!S|s}Jno^Uzx&)p z4zK=_#SFNhV&Af4@cbUOb%x?u0gu6Knc`v3Py`_!W+I=HdFQQ09y;SP1c`PY20YN2 zlt0C$MNo4{_-#xAhn8HUuya}jiM zOx7Q9H4hVTV)xly!9+WYHlQ1vbFfYv}=1r!dRNYc&GJ~m9tfh$5 zjASIab|J=V^^`R67umK$X@c`~HBn^EpxEt6{TH%`;8G?E8K~fLr$4`-T$5L#c{!S+ zm@YaGIEw)&TV>^7d(S>mWKd0|J273ty0oi5ZMz>WDeJF5O?uLm_&&`{j%)z1^Q3KG zGdn?#^U7~x!S$c5w}2xvaf^gWx}`M~Ld59ev_Hpbb-@L5`)oo{jmn`N;32c@{X%6u zM+xV*;v0B_R6g5r@+kT**oAmDw8={Al9M1_;@QS2NByj4^cms0T;nG3U{Q82s@<_^ znW@$U02?lI@VoX%RLM76-y?@-?p~G8ue1ex#~cK;^VdITUWuxsQ4_BsYv5U6?5xFP zt`2qsYu<48@2oSq!2bz(WWE%LM8lnUuG-736=$?be75@p* zU!K;uzusHp2*rF1iiL+PF{CNK^Rz@7lczsoT;W6CRfJ#NThG3G^QD_0>jOD&iM^4`7PAH?^DwdApRlpMl<%u} zwWzp1;pzYL_%SSa{cjoRzvv3>*3Kgs1A*<(-+bqvaqIsNe+ZWF{wEgtLz6-lOir#R ztKHk=-!~XmTMfRqBvEIrz$l2Tw~95NsQ7fCx|~0?+-3gK%xdGgYV4nb*Q8q8{cU;t zRgvj1vaj0v%0Cxc^qy;^$zLUJdeEH9!s2Y6^utEM#MeLX<`?ZHxV`jaPP_}UnlFEr zfB`@E<_hFtW}EjJQSaXFGNy{6_tc-&dL{by}UefVX{GSkj_wYy#orto_Pyg=%13 zKc2H0O5!{Rif4Yw<8Z$@K5_G(lqIL(%NK%&!xfj1NUOd|)|682g_?@k0>1Vb-fiQbX353ldx(wi&)owDGo`8lSiKg&ekZf*#8IoZoZUaax;Yss0vQR| zOtQ~Z9fU?uzpr5pPPW6^yDZt*(Q~l<-O1+AqH{YQ(5Tgu#oF^~B|7_!m|$ZC@U-Xc zddSp!Gw(nKeGmL|7Onvk<%S0^fwTBWfUlb(;WnWp#j9>#{^NU7*)UE~y8hWAfWZ60 z_g9fnB38JT4meC6N~`_X(-%);q352Ubf3x)fg`Z@XEW&50COi3E9*$z-9S96jTE`t zkNxU->!7*LTe~5^hw6{yK#V+v0Tv^FixHI-2Sx^~1>5%xu4-l4F@A8WaK|Ulp;CRwjBlL$~S-j!hS0pmSgdh{RXOc8HLwrR1+B3owvRP&w;2^YVz5v zNxIXqa>9!b{vdub5{j;W0+7TkKxHdyWp{ppf5VxDfP`8BtUDpE>)PNwFgI8Lf3-ZE zkgYv6c|F8J7f`!ljAqcRFbh3wv9XvgUbHU7!6M&j^0=r5B&U-5TU%P(S=n|maxtdOa zZZKA#tb)8}UH{*dM%!&8_COHpR~SouFTG;g9=ZSUZG}-7EYNTuGSqSOcU&DFmX^n^un!i^U3_IqNeR@@+|x1;}uVILY`%yb+wA&L`PmBVPAqs zkg%hVce-g`r0AY$oB~w9fbB$FgUcav(zHBha%uk+iaU!aE|D5H{$uM=5Ut_+b~MFH zNxZbPzA7_y0Wc=f+^`sNwe{kTRt7TM!QP6HuiS)iA&ROefOjkhy9iIH_<$9g`hp5s zV+>2+$k&4qj!mVLim0tkb?C~JMjvy5q|9Y)zvb?_AS@M5Sl6!WNl=8;59~3=N^m@l zr3lbxiFeGWdQY?OUjOp=y5rRoU~zf!Auk4-N3ZpP$;P_T%0e5|ARUv8kH;nkdlr#s z|Ac=3a`weZi`%^U!NOF(S}v2w2&Z+^nVN~<>Y8}2TW^~CPM%thLTYZ$(HET-Z?7#u zEIX!``gOH3IKy3G1pE&_A!E zz(H0l?bvB&#%%Kwdww}+x^^aV<6*PfMMJh9?h;|Sr+ba1?!-fzh%isK>+!YUE62Zu z08v0^H0=5oCzEHU);yFs_1I{t5^35a8$R^~Ltlw+Dc<$hjGXoOxaB-rdL@C6NbGjA zAda+PgVbZM?4FFwdhVAHgtXQu)C{WO z{YDxW0qu*W?Eyo=gW#o)do}cEx3F_^>qq9iyBGiD7*)WZ%@m=?dyyTsQD!>ar zJ$@1Nn9YPz_PyP5@Um{BH=cg52IQW|s@m#()QEDPFn3mLS62~J9Pdw%0U63Hd5YPV5@o|`1Ou(&IKNg2OoUS>f z4;BY0kH!gtQQT01WBu~)q1qhjDew&CUtw`B+(I3%vq{k)gS9reyFV<)VovJg8I0wu z)giyhWSIpwVU)voWfk0E_;VFB5p z@KEXb#n2UxjqL+$%)SJ}%JuT^`-bf?FQf}ec}iM@uLGT9w9^MKq(av8_A@ojcoo;- z6szAcpVz3KWY5_cP>l3@&?%xYlDV>gz6kh(2@r&AV6r`Ir`vP`EVBX0lUfhOPJm5? z{8B?p{t0!%CCJ@{xiLZQ1c)G?=Pna#cVP`L)iISHrL{cvs}2O&to19u(!53_-_!WP zz33RzfN3RgGRUFr?8**io#Wvs^LzGnU$(nSg6MiV7N^W704q$RE!0g>4eqf;r9T35@fo%mqk%G9vUQ ztG(JxUiT3h8-+cQ6N;gytZL8N!=TCAy4!oY;;C0_o!_q_#AOpcUkJ*cHv`c?cyZy^ z6B@}asA^Tw(O+xt0$$VM@NcC^17d#Qr9eP8XbO&wIG}RsNdP%(S>LC zr2M*f@$@GY6B7~J0P*?HIU=iklCvv2=sJqt8!2bzlckumLF6czDdyzlxRVL%vX+AT zyB-MZJh?Vhb6@*>KvqOOt^qsiF?_-dPg<-PCNFKCf!iBw_Y7)EUPKji+6mmDA=b_a zz43Zg3GUvV{S=#6?wUz7Z@WI*0?0^d($GcO&g*xOdjY|MgBlgXI%pP^CYv0x(u2B3 zv~z4EZ6h2Yk{j4C)A2#A1N7Q$q9TH#p+5*g`o&}Ff1Acicl(lrd-?@=9MFxx;XznD8DMjiTv?_szXj~XKj5EW*MEbP+t;sXq(AJEu=kG zVKrR(q(;Qm1%nMEpZHP8vNTu<^RDlsIU{(5UtbAGV=+Cu%^qTpS!Nx7MJ8nouT z7|AdeRI+9Sj*`@mD<801fnsD|G&5pUd5e~mJr>8kYdi=$GZ0O2GU?ffSjn5JYE>Jp zHwtxkI#Ln~Idclkh4!i4Fy&BKyPPs;Z!&CA`t&1#nXr1&e(m&kzfs0CAcyD{MN6XU zhbs?Ejrvt>9cJ?|Yf=-q=N2`O&fYmyy5Zd?KDkJC&^+iI3JnVms8X;@Ng5AnicFUJ z&a<{XQEkj@RXh$F71iU$S+iHE@NfH0$hW2|ca@_}P`?4Ac5Qh^)F!R+!|;A< zq2p)T(DBH0&)=a%T6JTS7w5Zm%Dk|K%K6mzeEr8tGO%6*YH(hxj2XS}g|ZAR_S^Xo z<}H6JQq=mhuNLZDhE2{^Y2SH6jl(k_pg%(Yz``QfVN?XEZksy_)i+R?^w`lnNqIME zTjcA!Vfg-0Y@lZjX;vgDL8T zOsJv_VLKB2?QPLV>)1-e^{qj+;8+L!_y}#@T_kY(I}1N$gcmd^Uy!lyc%;ak_$kN0 zj6?6pLdsxhEsowUvQ*?{skaF*Sb@>SeCjsJsfepbTk}$sC=6z=f(F&`W??JLI7}mJ z4liQNxEmx2Pd(6e$N2Uy5@tHp%_T3RFbeVHYHwOAyM!DcsLnkt&!DOOnmG6*gkSkR z|HMO|qSs{2!!MO%6@}(c~cQYBsjwtGCt}%B9!pTX`7iyKXG?86{Xgm5+1H zyKo^W_V5JmFBB4&dd@c&r*Pk1Z8cjs+=~y&By5P@TmO6B+82^vNNgH^(uIE5k51(= zpLqHwvp8R7C=+)g%rNkoh$H1_jac#GE}WRtUdFl*P%@Ba`p;pRxKErryh;pp%p@UP z2*sUfd~6#Usw?awjYi^l2vmau187aK{AtCj?{(33-`3qFkx`hjr?F7^dhm_t7>5Z) zx^%wYh8DSOk994=#d%%k>%sD;7q93&@?r6kyiwa_g=)h&NOcvmgz=b2o$O?xT|?L%;N0P#^_fzES;RB@ioFpe65@i@LX zUj1Rt@rUDU?&o`EFdLa@aTH_}a-mqjHm*y;;ZQ5znX9sxJFpu2A#<>V`=sCSA{eC6 zW!4QPMxZ(51ewr_04n%hdcAa4L^mQPIE#$6UrZ4 z(u*l3sH>Mu;pR7KTAwp?f2~{XrtLm`n){}xnv?g^v0!n{H=6h9m^v)D#3j-<^$tu{ zLCY;RVVcvP^HbDVkBRpj3OQ#0u3*-+&sqMHnK2}jr|C%Ar2GG|%m1HXNqTe=H1qyz zv4Q`RdHt6t>c5pY5-+!$)vxvrnEiM&ojqineDBVS!>D_d|K&bkrLR!dsG{8HFZeeV z;=1&_qRL{3%lcFEa(6gF3FQ;UB-J8)wTh3Lb@qQDyOcX~;ecF&ZT=4>v8;P^Gi0o=?PX#juJI*I*eCL-6d#t8?roa* zjE(xGN72}<=3YWUDQVdkO-Ez}8=b7uz0S)(z_?XfQja{3RWY zZYPDnVR2rt>v>Y1dw;o(1qB>!ab$n4O74nWy;y5|xI(Y(5kyvaC-=K0Sa<=|3TE&X znvG{WAJjqWJ>c&)Zb64D>{@F|kA7d2Yqa=GE;xtL=?@nG=B`@54#3)WmLiEbx0`?NEIQoNqWlc)dG5B2$FJP`5^m0`5<+wPdE$9iJuY~ z+;3&CxJ1Hf8qOssc}ehw46Fv*Q4`w){t}aQlEMR@wUuSzQ7IzXu{VPMHC8 zQ1Jh`iV>mRwE>;4AixF@CuQ>iyT2DipksS%%{tui5yRmgH(Am$WPrmLbh$<1MFYe$ zt>b-VL;~W^{MCA+FP|z{yHQqI$Y@x*Ny*{vYcd6i|)=w3G^B zY6e`Mm$tBVCa9N|LH5onn9Ny2dK8Zr$;)FqEB;*=KC{;H5@>R$b~`b?081b!d4BmB z5tM>K?w_wJH~h8w?XxXdo4vMe8-g=v{HebPnUf&UHiXTDSl9ln3-})39!*_2X%&W~ zI9M*!+JRUc;7^NeLLb9a!=6$`{~Z$AYUekVU+8kyp0r@zpLS6yr*T$ZKH zUQoU$W8bgE4wOfL>iOi_+zNpM9K%x!(GUE5E}oM;l-km{$*iHznXg{f^zIPXVfop( z$$b9gZXsx}nktmJt%YP`F#eu@TM}5iB$t&DBmQwRJee@w1^9fa*zsuSyTxQ_4fujMm`rtYlA>y5xVaUpe0i& zQlt>9%R%8jqW3^9osi8xAeULEaR$Zn<;+3RsV%lfIzubv_otU9dS!@IFIOZR9Uy!) zywo+GS3b8oo%e~Th#{mbXBim`9^sj^6FBo1(=sZf2B{rbQ=jH)L_;#)Lqd-tKiJGw z(DYa`EHssn=pQz@tAnLjyoEZVvD#nhy}$FRITsc#j#WrzPo(4qtBF945d<0%Ww}^l z{Uf%p=Oy%D%?#Wv&{%aHyTtLfz+zL%IdB@A@Hn!)+}e0IzAEdJL-0ljj3WfNLRGG?wrZTOy^kA+l=Ub2$l4u4gDX*2H`^>ZsGnUjBNicl1aDVhgefd<3U9Ew}g_-y5G!x9*Cl`CuR9XeIazU>OV4-s7x^BWi@E zz}djB`X4nfmeOnvA}h)E`ApQQdy43iIzStJeHBx=Kx>)CS#TxrQ5)Df@wiMcE#=g; z-=oym)$-U`ipO3}8=7*QeOAD9p^$?6v-m^B9u8L^{B9bd8`H5wA(&0{`Gyn{i$4t6wppJdOIF2R^Cg-j|t z>lK&oRt6qyRE8zqE#2jsg~>9aqHTXDX^M5M&j0Up(x*4< z?0=<`KK(156h`{(TPy0LBxDT_Qbnd_kntO?H|BdxG@;Q@rU#aTAW2ao5yoo~zC9gZ zbjIx`wEPQQDVK(vf%I&=%p${DP?yj6Ywo(;(Qa5jk5M*~L}KAJLz-(lN%Ahjbro$84Yz1#i)l|*0eEoG_hQP^S5VHfVHD`dqxWFHBNKZ4%Q64)yM zVt8E9r&A!7l*~s{ezv&(^5G8CdA0N=*LCd~0h zkM9Y-nZPdFI}{zQxk{uk3f5PbP6D<64{>iDRpq~~@6sKU2I-bALFo_yDW$tXq`N^< zN$HdZLFw-96p@tfZUJdHpXqn4y=w0<&e-R7&iTh-!7S$c&i8um`+Bld2;I&nHJX0g zlfkDQdmP0a(#KdWAHHdCcddO99%{0|)88Q+)fdR*DE?KKC<@&FS&VrZ*)tz60F8rj zuYw>c^sLN;aW@a|8qOPCLsBaV*TM0oc{IK{VEAIlrrqWh{%ll%RT?dx&5*k6xuunU zhzeqO+@FBbS7*XWg?Nt09X}@Riqg2ZUf>CoY)||k$PD#1%6XeX(-3_N4o{y;q#1?9yQ`EhWGk!d55r4I#TWTkH)$zt zb~%T?< z_-w}97Jzy&@@R$@ID7|`lXK1_@_p*PgdVLkMGwmmwq%-TZ1eZ`e)-JC<+J${&y%`A z2}MbW)9Gp8%SA0Y(6;JRkTZM29sLrH_PKVv;w^q%kkrbzPcI@o7(KBZi7Q2zSBR&6+YMO)jpq^Wo?n;pe7YetWWaJpH8RIhN%b z?i#K`i+QE8?qAuZCm_4jOZHYYJp@tl>t>CmGKC2omjSGLR3K?Nj3Q9=8iVa9h$OBB1Q&@E_3NL_w*9&yQP;$ zCJ&G3ganP{yS0x~2+vk&4gyAGf^x{k__+2gkzbH!fB7l%tT^xE-^f&47{TzABxLiH zy=AUpM#X>aHok`Ep>nFC|KaM;90wUoH(dL8h5aUsfZJ^e8lY-kG+e>{9|BZLmsKFM zsHim@D3oSb;KkDNE#up_#g8H|%b!-lYgLOrHQbDVWYg+qcTZP`HY1;+o=x{+-?v;P zz>{{}RNlq!l}kRR*SzawqTt3kVTYOPN6o6#3|-P zL%h2Mr6y~TRxka~VDLSC_bre4$oyfms$y=rJPi~M42YFD>vuf6j<={9cRXB%V{@2R zz;4R7`;UMXu~9|eAJ#_tCF7||)?=o@N1H1wc@;U2=90h2W4Z<%fX0HOTJNp)YN$ku zJs}qeHmQ^Xs&!g)=e-T6R?QRUY(TYYpj3;cBaAU9hxKn%tK!oisui0jyU|VfmPrws zs=9Jw9%=zCiuc{rxF@y91ExahF2h=UHNaU_s!goc^VOU}p&0N2BIY((EhSFEnZVSw1-C4Tmdk-FMp%N2GcG8p!_sZ~O`fd@RSH4?k47D1WAKa)yVJyw9ZM zQ%Z}}h-pLI`Uk`eTW*3^ePeIhmy?dVKlQLFtWp>SzBRlX`NTUk3nnnzr&(4RbnP;> z7D3#EcntF8k>rB;KEAVLtWkrvHrG$cSg*j~vVGE-Ll#P`bbkOJ$}^CpLOik~w|}Q? zye)3u&~`SVpj3yvl{cOk8@-Z2C@yW!l$XK+aVv$OB))#z{io-Z75R*i>%^S(H&uO( z!%F~8$R&n8FrdK)gyJzaPL7PVh{8ptl5;VTaf8um$W4U+iF)gIY#FFvgoZ>jAD<YcubtKmET)tX7J`J^s1w<(26?_A>iGvyyJ;Dl^XPzxrN(mv)4i;E?<` z(hj-{GQJ{>Y3q|%^0~-s7xY=>Ld5iB$6{vt6skq;oX`Ns=ElbR#j^h?Vx|3mKVoIJ z7}+4=aEfs7HJ0t)D`BNGRpw3We3f0`O5vhjr80!F%1&T<G+}1WteY0{QCO5$4hVjQK{aT%*)Z2K_;!woQ8O83QSUH zH+6Z)IoV3hzi03PUt7`-%zrg~)RcmYqx^$KXguVQO(|R6)i;^PJQ9dR}HUVEUR_q_Wk-He%y8fNBx_ZbQ{D1h$h~l)4G8TllWTH>=bN-AHzK_ zk5?>O*sUD?zYbab2>;Icn(8VNpKpK}F^)~Ek}ems*r2WT0Dys7pba3@`I6?)OEOhsr2#desNcqdp+6HT zuVs7-7#Q-I^kSrn`o0^!o2#?4g9;ulZ*Q^Mt~N<^DvaOnK`GVTBk+IK3?`UV@}Z7P zi6fA=*`56fMJ)I5H~*|gDPWUCsI}$2aZ(Z=)5$k#FSp3m>O!LDabTwxmmUTVDg#zb zN$Yxb5E?prJ!}pTu;RQhI9F8zbB8w6)RuVwnh>by0$Y!s*=O-uO%1!w**s0 ziL3zNZD#=7uOW|>oPhoF)c}8Z_z)>fxs1Gw*en2sK)C8Ps|;%6%wN%}?i{a+L)Vq} zL<~Nq1u9==L2JMk+ISZ^`8xRb|Z2{N)ZPq0!%@AvYTI-Z5dz@)zwZfpuud{@q4So+fL8X;+H;q&emRg&dyDrOAVMFVnAw`iRerh*`X1n zzxIRh6#E~{rpiHAc^pgVSgZ-uze?r}oJjvJ`%K?9900%nV-9V+tR%_ETST zLw+yt^6NIwg`Wb~=zLpuzDEe&-->k?eo#1d0CIA*IJ8prPEKSMt5EA_=;S{Y>s*^U zq=XFo&PnMBw5^0K z@p^Peqk9zn8i14%V1Z>X4mbw9&JA$0s2QM#N;m8cTuPygrgK~h-CvvQJ_`EY3a~@n z9q?p22c|=_9hlocvF(uVh(m!EY4$y0a~7jxnKv@f82zc>-C)iC1FnU~!jK$j_J>B- zS(XS3I?CT$Rp0PGvTHs~nJiYC^mG;lIvWvTBW&@>LV9yqodWzbkX#>wdKNG2ktlkB zcqK+5Luf`=487G}XCJ(M+ycG_;>+3pGj-z&Qyc0cjM)cSxovHfrxHgm?i=??oe~eh zeccXn2G7sPtj}M8FTz>BNv#Uw7v>(^_U)fueE{#{nGwOG{gFUFdr`M}hu{V9RM;M` zv``GtwY^_LKRnd@Z-Cp0i61^D>qZQ%^el9S-l*Pu^QKMmH!MN|A6I*a^5#W%`yRBy zr!LINx$g5s{MCj&j(hRD2lC?^ggChO{bv1dUzu9Y#_v>>T(~^1tpdWy2hk5L77yNA zDx|+hYic$2^o$Q*k*crP<>{k3v?R%jGlpUmk-D+4A3;N5)a*pW`P75F_rH3Mm+3QZ zL96LQEp30f%bxm{2oR&qAYH)D7wE&Fsx9}v11Um@93@~}5`+fBdZHd1vWWoJdnk@o zAA@sgiavnCuGjcijZno`&KJ$PF$ntU}1OeB~sJo34u{tzJmA&*_Jv0E6qR0*v)T63d^jK$`8njQ1> zbI!*YPWu&oA4K{r*PTZE(KA>bDTjhTuCQQt!&*^VrQV#p)Nfx#EFbaz$y2%j5*}=} zQXYlVVnGAD*qwLr=%;p99TCVEqHshue(yQs;2pX!pFT_C4UChVmGr{3aAOE{Dbu`l zC+lp3Cw1+fr=T9gz^YV8H9iXD4DK7YQZ2LO5G7w2lUlYr@O{_%lhSBQ-?6SlyQMbz zC!WU9+)YIN!QceOcou90o1Tje{r2YSYIg;W+NMF=&hP|}0RN9dlMr_>v`9CL3OV-c zZDf?9kn_gb1838pW>40X=~X@f-K%cp{>zE3Jb6caRIL#FP){(7u*~k{IuRpdZ&=4A z!p-tA9f!)R*~RlfXMXXTr} zfZwlPDDWc(!0%-3Eo5B!Oy)6d3g-_#`pQ>@#|y@Ogk_{(1|wQNm&kKDMg{D&4mT8roY)! z_hZAPoqfG^6iizkgeN%VInOdQz3Xfj(pB{YcEw&jlAU z>_3LTyY!hwLV~;S`6MGgiy5}L8+czwisB2=$7}YC$I7JQ&M>OoKIzg7pg7XsaWlT^ zID(;|3(^bGee;~moHo>6eD9Hc6(bF(^&kCck)ys4EOi@!HZ*f7>Ks%y4^umCz(h3e zHwt1K;;|G(ja=t+O$f>Hm$1wY{?>I)UI{u>u`d&$}a%n5p4&mv(JFPkr9 zE@Jz6?P+-o4F%_DYGBI~N!FJ$`qsqxJRyTkQUM<8syOob3ZeTTw|&%ohXtd8>EV$a zM-kad59k>zM)n17gU$2G83mERI(724O)qMFnecXU_BJ)NmC8w6@N9)TE%VH>8 z4Cb`6N8Gb5%5@nKNCqhqrD?zN7bw@b-UcZGHEl-ligX=@I19160(@@(2jM+Grh*_5G($iD&~9sV~Jj z2(%90X}w&{{EGFz0 zc`!c`%E~RC;=L93q(YGK8}&lXu7i=qqjiUns<(eYd{KtRe6d@x_#P46Q58S3h02wE zH(QPXLoY07(F=8l`m_Dkf>D$&1JCJq`8Uc8M^#hBbf?oT=D*r*SZ?MZR`qDY9;m4}S%4>n|f+8H|@iPFCgX%2e3sBDc;*!zD?wAVZCuB3HIj{@l zXJ*5t6*>Jdnw(SA>f%pV&>FwkWMMW<6x;{>rV#N!(};c2`RIgjPnd11?tg&r-DD$R||pAI!mP6mJO-}B+~>y_HG{TUU71+G%s zgOIV1FS)i)cTG(!ab#VLF`ecT#>#UZ)f(M%Se8Hk37s#iwaAsxA2NUK`q(4(8W&$B zWgqp)CflAz@8T?_TK{V!4C9E(T5#>BO0T(sDqewSLIICJ<`datse3>xf#Q%<{`wSA z`;EL$^G_NUn1GK&tS)Q63d)DmPu9M(q)$0wW1rfjvZy8s4%$8H>7aNs_wkZDBhqEK z!g?1piSi2Vv$ctKQv`8~Y!@1}hRSkeP}DmdHAaniO|tP=JB_jHjb9_-pMGp<5MAXX zj?i`b#yfRE$fo@nfxK^btpg0X@~?%2gys)KXo4Ubp~e0?Z%4LrpO;hbnowqH%TM^H z_|3tgi#Yhip4G+?Blnq@VlLG-becBM4qy`2*PW;dktgyYVR&9CYF63H(K=uo!ArMa z3)?UIb@%iTcr0yylE%#vu6q(RMiLqtI#y?AP<9mM#g|Cl)BN_VfUSI;h(o{FvN7p% z6bf~>sxzEYj{+Uo8ISUE6a)Xb*TT?G4eeZ%6CdPc{@ zG;sJ%^Bi5hbp+eK4tks99$&Q&x#oJJf=$9L zSp%Eca8W{V+O>7&= z*jzL>Ax^sEq=wq_fNGioO9pc;X#`*bEBA2>3d*H#LP*p*9*KC@PtVr69%xVUX}lj> zxa$CAQB#oAv7Dtsg6oAv{Y4r5%{G;2*%o?>UnV{h^1H${2G2!poP<_JJ{HTeEF1BdqX~tU#3wv;LswA z#yw2R3*q;|dSCACRT8rMzQW7bg%3%6iUX&3zLKyITr^)g`}%IvO30YGQxGwHeUG4p zkz~50ODj|Fu~nZ+Y>qB1_I=2T**9rrjP(LhF%X?+ft#v#|FJPP#55K^k< z@_fK9P`nB~)cB<(A_KU-77-A?_PCA=P(-2rd>g7FX4FhtmPQudlR@v;@r$P|tDNd1 zyKJDxx;vuk3kkcGLJI~fKPBjQi>`!$)6;tK3Hp^mum60132|`bmOw#F2ft9`J42Ig zfBqGMCIIbqKHi4@YjzgifBox!epz-_6*~B`|K=760oSg5Hh4zNvdJxX_}1 z`cwlfRr#EiCHCv|bn?~f8cQ`)6qMJZ@bK{Tj~~kvsuuk9_y*x2`)O%p4fZPmxc1V9 zsRyoX`_$CbvfE71$0tLAas?z;3CUgD7YLfa+*gDk=cA*evef&3o{)Fc>$S^5*J+U7 z<>j1hu6-)dCoZ0UW@bj#UC#&vO~8^E)^m^yTVhYrb4`)!jLZaonVU6NY0`%^16Fr{ zUUKi3r^MvsY~+Hj@c>gLXn%orz_~#_2n?9L277`*W0GhxzPLB-N+47Z6m3-BQlD<&WnR)lX3ox>H)!162UEV&1l~{5 zy(Nfi$TcXXnR~J#VT9C4OG;kmlRqgbb3 ztC4BD*EZU*!0N*qi2L!hh}`=w-<3=R=>O;lBuV@UeoWL@H$aXu_HdzT2!O-<)qWWE z8PIor0ft(%FJHc77C8l}R9P@7oNyJ|XOvIQ>V~KJ1yYbqL~O-EclO|ZH(sa2#^z9T zh73KQr%HBjcio+M>7xZqbSy5mWWn{n!D~@Aznm`UmgsT1`BGgyaW0XDVjJ4TbYxla zj|>)_ucP!n_P3DN45#jf5wlNUsA_SQ=ubbdV7UlBrAdQmGkzNIpe9MT5o`+2X{vUL_oCG#n&BW zr3KV<2SG%R1E}?)!Fa9E=a{@x81R32pWtK8iE;PG+neT)^gnipT8CIXVYuUW@2$m&cmZDAi{_Ur&=?sW>`&ON4W-B7gugPs3b% z42Bco2x0o&!Kb6UBgbPege3KhLLKS)kwla~f?qo{{eMk9c0-Iug~}vn`14unCnRiQ zT-g&mUC`g#^~G57nfv_A?}7Zlw(sa?q(2N&gVW}41y+d7kC~$pAxr!-M=VxGO|SCA zgR-xYQm@2sGXFax`psMJt(YFo(Z*VX# z33yLD0upWotO_9%tdrnnR}l_@m5PFhxgn6VzfHrw_*$Y+jalJVpK@`yP-MKd3^WnU zB=U8L14c5QK?EeEq*k8awSyuKp6r>p9e&rz43yG{AG_OQ?{)2Tg3%B~GV%J>uZNURybMrc|qR!u!l6#%7G8b9PiDU^ZsbP2bBYc@ZtQCcR|>i5o{oH zbjLtIrsdDw!%qZ)>r*u{0t1w8WfvbKFTuhhgLh)s^)GtMdmFSZW8i}wvd`q-IfS8Ei=CKln{M15cV$RxN{!ee2P88Z3a_`y$5&hMsA^gcWC1r}rk(T@7w)iiG{!#jnl&S^_=!vf*> zv4Cf*(x@;@=Lx_WUy)fiAJL|gvOhA#Po@93gR==1&SWE?+LEK7j-r}Ij-qNp1azj^ zS=nubA0ZCmIjdJ6h(_MeZQGVa}%7Ol36AN z!?vAsms0;3_`=UV79Z|g4LNfWTRtyQOL)JpDt29ur?Nw7=XP6Xc0(i!zr`2AcBX!m zn|HJ;>;WHyv+@HT;#&PN;1F@N;4)fC2q`DfeGl-dQtsjy!zZA_J?_+A6I$>}W_OGx zfFnq&m!UKm`6*f#6`nTyQ?A86eooo$5aqsK{)&oD1e#tD((1ls7-SY)zEY z{zN&1A1T+KbTz}e?RlyCGI;8L>d5qj+%G*Jo{za~gfDhaLk~p2_)MOzvXo?#Q_Rtf zyU1)MCtm00&bpfH{uMeJ+Vzio_E=zM3UMDL1esMwGad5E8^_RNmHJTLn|cb&QR)U| zKFC!AlY?wL1X&8{*}Oz~q7i*3W+X9RY=5+k4J(-m62&2OJAmVhwfkrV%y9e)oseR| zLrsmtnFW#GQ_u%RwHa|a2S&9;t5(}O>yN2IF)jk0HL$e%M74_JJ`&;3yzeI!>zoCr z?Hwh9oF%Gbsg63tP(PX9{&QGM*ruIp;(1-D&!XOpa- zyH8}3?uDL)9uR3^SO0-lL-_EUM!-E7<^Av*Q$pH;VUnNNeYClxxqP>gKYxL>d0DlzOc)Yhc|Nc3Nss|S z$E0ly9=3~S5x|fUjaY{#z@DZ&E;iru=jF4T&-{`Jk3?MFO}$*6!!_F80NIlEo=cZt zzifsyy#w}3RWHtDaS7^JN1+`#VTB(lSte+d2&Aa-Fc&u7GRqmuHk|K*mkIpK@styv zIoafj1l1Pm@s{esZguigZ-U1qevvmv9%!eUea4%>{ULHkIXN=~nOVR5EP^Dm{L;EZ z$dXpZyYt=FNV>|QN%L_zpYf3pgk0!WiIJjDLN~%?{6r7|Z5(XMdQw$LmayC-gLeTR zPN|-C;Nn+w-t+BnMqlvT_0NQx)2S^#R%acHx#ua=P_z(v-ee>jcn4J zv=6?KS7!;5P6#yBg-PV;+bE#>eW4l{&_97_2qQ*rN?5Usks9vCR_(c;Lf;H_^9C

    ? -) : Runnable, Cancellable { +) : Runnable, + Cancellable { val isCancelled: Boolean get() = cancelled.get() diff --git a/app/src/main/java/com/nextcloud/client/core/ThreadPoolAsyncRunner.kt b/app/src/main/java/com/nextcloud/client/core/ThreadPoolAsyncRunner.kt index 1fbed82..0c6da24 100644 --- a/app/src/main/java/com/nextcloud/client/core/ThreadPoolAsyncRunner.kt +++ b/app/src/main/java/com/nextcloud/client/core/ThreadPoolAsyncRunner.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core diff --git a/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt b/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt index 2e2c4e9..a0989a2 100644 --- a/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt +++ b/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database @@ -11,6 +11,7 @@ import android.content.Context import com.nextcloud.client.core.Clock import com.nextcloud.client.database.dao.ArbitraryDataDao import com.nextcloud.client.database.dao.FileDao +import com.nextcloud.client.database.dao.OfflineOperationDao import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -20,17 +21,15 @@ class DatabaseModule { @Provides @Singleton - fun database(context: Context, clock: Clock): NextcloudDatabase { - return NextcloudDatabase.getInstance(context, clock) - } + fun database(context: Context, clock: Clock): NextcloudDatabase = NextcloudDatabase.getInstance(context, clock) @Provides - fun arbitraryDataDao(nextcloudDatabase: NextcloudDatabase): ArbitraryDataDao { - return nextcloudDatabase.arbitraryDataDao() - } + fun arbitraryDataDao(nextcloudDatabase: NextcloudDatabase): ArbitraryDataDao = nextcloudDatabase.arbitraryDataDao() @Provides - fun fileDao(nextcloudDatabase: NextcloudDatabase): FileDao { - return nextcloudDatabase.fileDao() - } + fun fileDao(nextcloudDatabase: NextcloudDatabase): FileDao = nextcloudDatabase.fileDao() + + @Provides + fun offlineOperationsDao(nextcloudDatabase: NextcloudDatabase): OfflineOperationDao = + nextcloudDatabase.offlineOperationDao() } diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index db98e77..0102f52 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database @@ -12,23 +12,31 @@ import androidx.room.AutoMigration import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import androidx.room.TypeConverters import com.nextcloud.client.core.Clock import com.nextcloud.client.core.ClockImpl import com.nextcloud.client.database.dao.ArbitraryDataDao import com.nextcloud.client.database.dao.FileDao +import com.nextcloud.client.database.dao.OfflineOperationDao +import com.nextcloud.client.database.dao.RecommendedFileDao +import com.nextcloud.client.database.dao.UploadDao import com.nextcloud.client.database.entity.ArbitraryDataEntity import com.nextcloud.client.database.entity.CapabilityEntity import com.nextcloud.client.database.entity.ExternalLinkEntity import com.nextcloud.client.database.entity.FileEntity import com.nextcloud.client.database.entity.FilesystemEntity +import com.nextcloud.client.database.entity.OfflineOperationEntity +import com.nextcloud.client.database.entity.RecommendedFileEntity import com.nextcloud.client.database.entity.ShareEntity import com.nextcloud.client.database.entity.SyncedFolderEntity import com.nextcloud.client.database.entity.UploadEntity import com.nextcloud.client.database.entity.VirtualEntity import com.nextcloud.client.database.migrations.DatabaseMigrationUtil +import com.nextcloud.client.database.migrations.MIGRATION_88_89 import com.nextcloud.client.database.migrations.Migration67to68 import com.nextcloud.client.database.migrations.RoomMigration import com.nextcloud.client.database.migrations.addLegacyMigrations +import com.nextcloud.client.database.typeConverter.OfflineOperationTypeConverter import com.owncloud.android.db.ProviderMeta @Database( @@ -41,7 +49,9 @@ import com.owncloud.android.db.ProviderMeta ShareEntity::class, SyncedFolderEntity::class, UploadEntity::class, - VirtualEntity::class + VirtualEntity::class, + OfflineOperationEntity::class, + RecommendedFileEntity::class ], version = ProviderMeta.DB_VERSION, autoMigrations = [ @@ -59,40 +69,57 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 77, to = 78), AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), AutoMigration(from = 79, to = 80), - AutoMigration(from = 80, to = 81) + AutoMigration(from = 80, to = 81), + AutoMigration(from = 81, to = 82), + AutoMigration(from = 82, to = 83), + AutoMigration(from = 83, to = 84), + AutoMigration(from = 84, to = 85, spec = DatabaseMigrationUtil.DeleteColumnSpec::class), + AutoMigration(from = 85, to = 86, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), + AutoMigration(from = 86, to = 87, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), + AutoMigration(from = 87, to = 88, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), + // manual migration used for 88 to 89 + AutoMigration(from = 89, to = 90), + AutoMigration(from = 90, to = 91), + AutoMigration(from = 91, to = 92), + AutoMigration(from = 92, to = 93, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), + AutoMigration(from = 93, to = 94, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), + AutoMigration(from = 94, to = 95, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class) ], exportSchema = true ) @Suppress("Detekt.UnnecessaryAbstractClass") // needed by Room +@TypeConverters(OfflineOperationTypeConverter::class) abstract class NextcloudDatabase : RoomDatabase() { abstract fun arbitraryDataDao(): ArbitraryDataDao abstract fun fileDao(): FileDao + abstract fun offlineOperationDao(): OfflineOperationDao + abstract fun uploadDao(): UploadDao + abstract fun recommendedFileDao(): RecommendedFileDao companion object { const val FIRST_ROOM_DB_VERSION = 65 - private var INSTANCE: NextcloudDatabase? = null + private var instance: NextcloudDatabase? = null @JvmStatic @Suppress("DeprecatedCallableAddReplaceWith") @Deprecated("Here for legacy purposes, inject this class or use getInstance(context, clock) instead") - fun getInstance(context: Context): NextcloudDatabase { - return getInstance(context, ClockImpl()) - } + fun getInstance(context: Context): NextcloudDatabase = getInstance(context, ClockImpl()) @JvmStatic fun getInstance(context: Context, clock: Clock): NextcloudDatabase { - if (INSTANCE == null) { - INSTANCE = Room + if (instance == null) { + instance = Room .databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME) .allowMainThreadQueries() + .addTypeConverter(OfflineOperationTypeConverter()) .addLegacyMigrations(clock, context) .addMigrations(RoomMigration()) .addMigrations(Migration67to68()) - .fallbackToDestructiveMigration() + .addMigrations(MIGRATION_88_89) .build() } - return INSTANCE!! + return instance!! } } } diff --git a/app/src/main/java/com/nextcloud/client/database/dao/ArbitraryDataDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/ArbitraryDataDao.kt index 57afe3e..507e886 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/ArbitraryDataDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/ArbitraryDataDao.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.dao diff --git a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt index e04cc27..7f1c0c0 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt @@ -3,20 +3,38 @@ * * SPDX-FileCopyrightText: 2022 Dariusz Olszewski * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.dao import androidx.room.Dao import androidx.room.Query +import androidx.room.Update import com.nextcloud.client.database.entity.FileEntity import com.owncloud.android.db.ProviderMeta.ProviderTableMeta +import com.owncloud.android.utils.MimeType +@Suppress("TooManyFunctions") @Dao interface FileDao { + @Query( + """ + SELECT DISTINCT parent + FROM filelist + WHERE path IN (:subfilePaths) + """ + ) + fun getParentIdsOfSubfiles(subfilePaths: List): List + + @Update + fun update(entity: FileEntity) + @Query("SELECT * FROM filelist WHERE _id = :id LIMIT 1") fun getFileById(id: Long): FileEntity? + @Query("SELECT * FROM filelist WHERE local_id = :localId LIMIT 1") + fun getFileByLocalId(localId: Long): FileEntity? + @Query("SELECT * FROM filelist WHERE path = :path AND file_owner = :fileOwner LIMIT 1") fun getFileByEncryptedRemotePath(path: String, fileOwner: String): FileEntity? @@ -49,4 +67,45 @@ interface FileDao { @Query("SELECT * FROM filelist where file_owner = :fileOwner AND etag_in_conflict IS NOT NULL") fun getFilesWithSyncConflict(fileOwner: String): List + + @Query( + "SELECT * FROM filelist where file_owner = :fileOwner AND internal_two_way_sync_timestamp >= 0 " + + "ORDER BY internal_two_way_sync_timestamp DESC" + ) + fun getInternalTwoWaySyncFolders(fileOwner: String): List + + @Query( + """ + SELECT * + FROM filelist + WHERE parent = :parentId + AND file_owner = :accountName + AND is_encrypted = 0 + AND (content_type = :dirType OR content_type = :webdavType) + ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER} + """ + ) + fun getNonEncryptedSubfolders( + parentId: Long, + accountName: String, + dirType: String = MimeType.DIRECTORY, + webdavType: String = MimeType.WEBDAV_FOLDER + ): List + + @Query( + """ + SELECT * + FROM filelist + WHERE parent = :parentId + AND file_owner = :accountName + AND (content_type != :dirType AND content_type != :webdavType) + ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER} + """ + ) + fun getSubfiles( + parentId: Long, + accountName: String, + dirType: String = MimeType.DIRECTORY, + webdavType: String = MimeType.WEBDAV_FOLDER + ): List } diff --git a/app/src/main/java/com/nextcloud/client/database/dao/OfflineOperationDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/OfflineOperationDao.kt new file mode 100644 index 0000000..50817da --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/dao/OfflineOperationDao.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import com.nextcloud.client.database.entity.OfflineOperationEntity + +@Dao +interface OfflineOperationDao { + @Query("SELECT * FROM offline_operations") + fun getAll(): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insert(vararg entity: OfflineOperationEntity) + + @Update + fun update(entity: OfflineOperationEntity) + + @Delete + fun delete(entity: OfflineOperationEntity) + + @Query("DELETE FROM offline_operations WHERE offline_operations_path = :path") + fun deleteByPath(path: String) + + @Query("SELECT * FROM offline_operations WHERE offline_operations_path = :path LIMIT 1") + fun getByPath(path: String): OfflineOperationEntity? + + @Query("SELECT * FROM offline_operations WHERE offline_operations_parent_oc_file_id = :parentOCFileId") + fun getSubEntitiesByParentOCFileId(parentOCFileId: Long): List + + @Query("DELETE FROM offline_operations") + fun clearTable() + + @Query("DELETE FROM offline_operations WHERE _id = :id") + fun deleteById(id: Int) +} diff --git a/app/src/main/java/com/nextcloud/client/database/dao/RecommendedFileDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/RecommendedFileDao.kt new file mode 100644 index 0000000..a93f857 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/dao/RecommendedFileDao.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.nextcloud.client.database.entity.RecommendedFileEntity +import com.owncloud.android.db.ProviderMeta + +@Dao +interface RecommendedFileDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(recommendedFiles: List) + + @Query( + "SELECT * FROM ${ProviderMeta.ProviderTableMeta.RECOMMENDED_FILE_TABLE_NAME} WHERE account_name = :accountName" + ) + suspend fun getAll(accountName: String): List +} diff --git a/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt new file mode 100644 index 0000000..aac3d1d --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt @@ -0,0 +1,30 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.dao + +import androidx.room.Dao +import androidx.room.Query +import com.nextcloud.client.database.entity.UploadEntity +import com.owncloud.android.db.ProviderMeta.ProviderTableMeta + +@Dao +interface UploadDao { + @Query( + "SELECT _id FROM " + ProviderTableMeta.UPLOADS_TABLE_NAME + + " WHERE " + ProviderTableMeta.UPLOADS_STATUS + " = :status AND " + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " = :accountName AND _id IS NOT NULL" + ) + fun getAllIds(status: Int, accountName: String): List + + @Query( + "SELECT * FROM " + ProviderTableMeta.UPLOADS_TABLE_NAME + + " WHERE " + ProviderTableMeta._ID + " IN (:ids) AND " + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " = :accountName" + ) + fun getUploadsByIds(ids: LongArray, accountName: String): List +} diff --git a/app/src/main/java/com/nextcloud/client/database/entity/ArbitraryDataEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/ArbitraryDataEntity.kt index 851bd63..d996521 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/ArbitraryDataEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/ArbitraryDataEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity diff --git a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt index 3b53e96..56f33b1 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity @@ -122,5 +122,29 @@ data class CapabilityEntity( @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT) val dropAccount: Int?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_SECURITY_GUARD) - val securityGuard: Int? + val securityGuard: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAME_CHARACTERS) + val forbiddenFileNameCharacters: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES) + val forbiddenFileNames: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS) + val forbiddenFileNameExtensions: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES) + val forbiddenFilenameBaseNames: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT) + val filesDownloadLimit: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT) + val filesDownloadLimitDefault: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_RECOMMENDATION) + val recommendation: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH) + val notesFolderPath: String?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_DEFAULT_PERMISSIONS) + val defaultPermissions: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_BUSY) + val userStatusSupportsBusy: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_WINDOWS_COMPATIBLE_FILENAMES) + val isWCFEnabled: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_HAS_VALID_SUBSCRIPTION) + val hasValidSubscription: Int? ) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/ExternalLinkEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/ExternalLinkEntity.kt index dad89f3..03da555 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/ExternalLinkEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/ExternalLinkEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity diff --git a/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt index bcfbf6e..175287b 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity @@ -50,7 +50,7 @@ data class FileEntity( @ColumnInfo(name = ProviderTableMeta.FILE_ETAG_ON_SERVER) val etagOnServer: String?, @ColumnInfo(name = ProviderTableMeta.FILE_SHARED_VIA_LINK) - val sharedViaLink: Int?, + var sharedViaLink: Int?, @ColumnInfo(name = ProviderTableMeta.FILE_PERMISSIONS) val permissions: String?, @ColumnInfo(name = ProviderTableMeta.FILE_REMOTE_ID) @@ -73,7 +73,7 @@ data class FileEntity( @ColumnInfo(name = ProviderTableMeta.FILE_ETAG_IN_CONFLICT) val etagInConflict: String?, @ColumnInfo(name = ProviderTableMeta.FILE_SHARED_WITH_SHAREE) - val sharedWithSharee: Int?, + var sharedWithSharee: Int?, @ColumnInfo(name = ProviderTableMeta.FILE_MOUNT_TYPE) val mountType: Int?, @ColumnInfo(name = ProviderTableMeta.FILE_HAS_PREVIEW) @@ -115,5 +115,11 @@ data class FileEntity( @ColumnInfo(name = ProviderTableMeta.FILE_METADATA_GPS) val metadataGPS: String?, @ColumnInfo(name = ProviderTableMeta.FILE_E2E_COUNTER) - val e2eCounter: Long? + val e2eCounter: Long?, + @ColumnInfo(name = ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP) + val internalTwoWaySync: Long?, + @ColumnInfo(name = ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_RESULT) + val internalTwoWaySyncResult: String?, + @ColumnInfo(name = ProviderTableMeta.FILE_UPLOADED) + val uploaded: Long? ) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt index 146d086..7247ee1 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity diff --git a/app/src/main/java/com/nextcloud/client/database/entity/OfflineOperationEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/OfflineOperationEntity.kt new file mode 100644 index 0000000..1d5b151 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/entity/OfflineOperationEntity.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.entity + +import android.content.Context +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.nextcloud.model.OfflineOperationType +import com.owncloud.android.R +import com.owncloud.android.db.ProviderMeta.ProviderTableMeta + +@Entity(tableName = ProviderTableMeta.OFFLINE_OPERATION_TABLE_NAME) +data class OfflineOperationEntity( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = ProviderTableMeta._ID) + val id: Int? = null, + + @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_PARENT_OC_FILE_ID) + var parentOCFileId: Long? = null, + + @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_PATH) + var path: String? = null, + + @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_TYPE) + var type: OfflineOperationType? = null, + + @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_FILE_NAME) + var filename: String? = null, + + @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_CREATED_AT) + var createdAt: Long? = null, + + @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_MODIFIED_AT) + var modifiedAt: Long? = null +) { + fun isRenameOrRemove(): Boolean = + (type is OfflineOperationType.RenameFile || type is OfflineOperationType.RemoveFile) + + fun isCreate(): Boolean = (type is OfflineOperationType.CreateFile || type is OfflineOperationType.CreateFolder) + + fun getConflictText(context: Context): String { + val resId = when (type) { + is OfflineOperationType.RemoveFile -> { + R.string.offline_operations_worker_notification_remove_conflict_text + } + + is OfflineOperationType.RenameFile -> { + R.string.offline_operations_worker_notification_rename_conflict_text + } + + is OfflineOperationType.CreateFile -> { + R.string.offline_operations_worker_notification_create_file_conflict_text + } + + else -> { + R.string.offline_operations_worker_notification_create_folder_conflict_text + } + } + + return context.getString(resId, filename) + } +} diff --git a/app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt new file mode 100644 index 0000000..6501797 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt @@ -0,0 +1,72 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.nextcloud.android.lib.resources.recommendations.Recommendation +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.db.ProviderMeta.ProviderTableMeta + +@Entity(tableName = ProviderTableMeta.RECOMMENDED_FILE_TABLE_NAME) +data class RecommendedFileEntity( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = ProviderTableMeta._ID) + val id: Long, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_NAME) + val name: String, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_DIRECTORY) + val directory: String, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_EXTENSIONS) + val extension: String, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_MIME_TYPE) + val mimeType: String, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_HAS_PREVIEW) + val hasPreview: Boolean, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_REASON) + val reason: String, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_TIMESTAMP) + val timestamp: Long, + + @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_ACCOUNT_NAME) + val accountName: String? +) + +fun ArrayList.toEntity(accountName: String): List = this.map { recommendation -> + RecommendedFileEntity( + id = recommendation.id, + name = recommendation.name, + directory = recommendation.directory, + extension = recommendation.extension, + mimeType = recommendation.mimeType, + hasPreview = recommendation.hasPreview, + reason = recommendation.reason, + timestamp = recommendation.timestamp, + accountName = accountName + ) +} + +fun List.toOCFile(storageManager: FileDataStorageManager): ArrayList = + mapNotNull { entity -> + entity.id.let { + storageManager.getFileByLocalId(it).apply { + this?.reason = entity.reason + this?.setIsRecommendedFile(true) + } + } + } + .toCollection(ArrayList()) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt index caeb875..ad5005e 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity @@ -54,5 +54,11 @@ data class ShareEntity( @ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LINK) val shareLink: String?, @ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LABEL) - val shareLabel: String? + val shareLabel: String?, + @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT) + val downloadLimitLimit: Int?, + @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT) + val downloadLimitCount: Int?, + @ColumnInfo(name = ProviderTableMeta.OCSHARES_ATTRIBUTES) + val attributes: String? ) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt index 8a4298c..bcc9d4c 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity diff --git a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt index e4c59c1..1417720 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt @@ -3,14 +3,20 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import com.nextcloud.utils.autoRename.AutoRename +import com.owncloud.android.datamodel.UploadsStorageManager +import com.owncloud.android.db.OCUpload import com.owncloud.android.db.ProviderMeta.ProviderTableMeta +import com.owncloud.android.db.UploadResult +import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.lib.resources.status.OCCapability @Entity(tableName = ProviderTableMeta.UPLOADS_TABLE_NAME) data class UploadEntity( @@ -48,3 +54,27 @@ data class UploadEntity( @ColumnInfo(name = ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN) val folderUnlockToken: String? ) + +fun UploadEntity.toOCUpload(capability: OCCapability? = null): OCUpload { + val localPath = localPath + var remotePath = remotePath + if (capability != null && remotePath != null) { + remotePath = AutoRename.rename(remotePath, capability) + } + val upload = OCUpload(localPath, remotePath, accountName) + + fileSize?.let { upload.fileSize = it } + id?.let { upload.uploadId = it.toLong() } + status?.let { upload.uploadStatus = UploadsStorageManager.UploadStatus.fromValue(it) } + localBehaviour?.let { upload.localAction = it } + nameCollisionPolicy?.let { upload.nameCollisionPolicy = NameCollisionPolicy.deserialize(it) } + isCreateRemoteFolder?.let { upload.isCreateRemoteFolder = it == 1 } + uploadEndTimestamp?.let { upload.uploadEndTimestamp = it.toLong() } + lastResult?.let { upload.lastResult = UploadResult.fromValue(it) } + createdBy?.let { upload.createdBy = it } + isWifiOnly?.let { upload.isUseWifiOnly = it == 1 } + isWhileChargingOnly?.let { upload.isWhileChargingOnly = it == 1 } + folderUnlockToken?.let { upload.folderUnlockToken = it } + + return upload +} diff --git a/app/src/main/java/com/nextcloud/client/database/entity/VirtualEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/VirtualEntity.kt index a8eb01f..d8c7efe 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/VirtualEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/VirtualEntity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.entity diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt b/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt index 225aea8..b520b47 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt +++ b/app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt @@ -3,12 +3,14 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.migrations +import androidx.room.DeleteColumn import androidx.room.migration.AutoMigrationSpec import androidx.sqlite.db.SupportSQLiteDatabase +import com.nextcloud.client.database.migrations.model.SQLiteColumnType object DatabaseMigrationUtil { @@ -17,6 +19,32 @@ object DatabaseMigrationUtil { const val TYPE_INTEGER_PRIMARY_KEY = "INTEGER PRIMARY KEY" const val KEYWORD_NOT_NULL = "NOT NULL" + fun addColumnIfNotExists( + db: SupportSQLiteDatabase, + tableName: String, + columnName: String, + columnType: SQLiteColumnType + ) { + val cursor = db.query("PRAGMA table_info($tableName)") + var columnExists = false + + while (cursor.moveToNext()) { + val nameIndex = cursor.getColumnIndex("name") + if (nameIndex != -1) { + val existingColumnName = cursor.getString(nameIndex) + if (existingColumnName == columnName) { + columnExists = true + break + } + } + } + cursor.close() + + if (!columnExists) { + db.execSQL("ALTER TABLE $tableName ADD COLUMN `$columnName` ${columnType.value}") + } + } + /** * Utility method to add or remove columns from a table * @@ -46,11 +74,7 @@ object DatabaseMigrationUtil { /** * Utility method to create a new table with the given columns */ - private fun createNewTable( - database: SupportSQLiteDatabase, - newTableName: String, - columns: Map - ) { + private fun createNewTable(database: SupportSQLiteDatabase, newTableName: String, columns: Map) { val columnsString = columns.entries.joinToString(",") { "${it.key} ${it.value}" } database.execSQL("CREATE TABLE $newTableName ($columnsString)") } @@ -80,11 +104,7 @@ object DatabaseMigrationUtil { /** * Utility method to replace an old table with a new one, essentially deleting the old one and renaming the new one */ - private fun replaceTable( - database: SupportSQLiteDatabase, - tableName: String, - newTableTempName: String - ) { + private fun replaceTable(database: SupportSQLiteDatabase, tableName: String, newTableTempName: String) { database.execSQL("DROP TABLE $tableName") database.execSQL("ALTER TABLE $newTableTempName RENAME TO $tableName") } @@ -98,4 +118,12 @@ object DatabaseMigrationUtil { super.onPostMigrate(db) } } + + @DeleteColumn.Entries( + DeleteColumn( + tableName = "offline_operations", + columnName = "offline_operations_parent_path" + ) + ) + class DeleteColumnSpec : AutoMigrationSpec } diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt index 9d025b9..d975cb4 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt +++ b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.migrations diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java index ec93cd6..4cbd78a 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java +++ b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.migrations; diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/Migration67to68.kt b/app/src/main/java/com/nextcloud/client/database/migrations/Migration67to68.kt index b7b10a6..5e7ad66 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/Migration67to68.kt +++ b/app/src/main/java/com/nextcloud/client/database/migrations/Migration67to68.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.migrations diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/Migration88to89.kt b/app/src/main/java/com/nextcloud/client/database/migrations/Migration88to89.kt new file mode 100644 index 0000000..3034ae2 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/migrations/Migration88to89.kt @@ -0,0 +1,31 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import com.nextcloud.client.database.migrations.model.SQLiteColumnType +import com.owncloud.android.db.ProviderMeta.ProviderTableMeta + +@Suppress("MagicNumber") +val MIGRATION_88_89 = object : Migration(88, 89) { + override fun migrate(database: SupportSQLiteDatabase) { + DatabaseMigrationUtil.addColumnIfNotExists( + database, + ProviderTableMeta.FILE_TABLE_NAME, + ProviderTableMeta.FILE_UPLOADED, + SQLiteColumnType.INTEGER_DEFAULT_NULL + ) + DatabaseMigrationUtil.addColumnIfNotExists( + database, + ProviderTableMeta.CAPABILITIES_TABLE_NAME, + ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH, + SQLiteColumnType.TEXT_DEFAULT_NULL + ) + } +} diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/RoomMigration.kt b/app/src/main/java/com/nextcloud/client/database/migrations/RoomMigration.kt index 2da2ff0..1d41116 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/RoomMigration.kt +++ b/app/src/main/java/com/nextcloud/client/database/migrations/RoomMigration.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.migrations diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/model/SQLiteColumnType.kt b/app/src/main/java/com/nextcloud/client/database/migrations/model/SQLiteColumnType.kt new file mode 100644 index 0000000..96777ca --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/migrations/model/SQLiteColumnType.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.migrations.model + +enum class SQLiteColumnType(val value: String) { + INTEGER_DEFAULT_NULL("INTEGER DEFAULT NULL"), + TEXT_DEFAULT_NULL("TEXT DEFAULT NULL") +} diff --git a/app/src/main/java/com/nextcloud/client/database/typeAdapter/OfflineOperationTypeAdapter.kt b/app/src/main/java/com/nextcloud/client/database/typeAdapter/OfflineOperationTypeAdapter.kt new file mode 100644 index 0000000..bd1ece7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/typeAdapter/OfflineOperationTypeAdapter.kt @@ -0,0 +1,96 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.typeAdapter + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonSerializationContext +import com.google.gson.JsonSerializer +import com.nextcloud.model.OfflineOperationRawType +import com.nextcloud.model.OfflineOperationType + +import java.lang.reflect.Type + +class OfflineOperationTypeAdapter : + JsonSerializer, + JsonDeserializer { + + override fun serialize( + src: OfflineOperationType?, + typeOfSrc: Type?, + context: JsonSerializationContext? + ): JsonElement { + val jsonObject = JsonObject() + jsonObject.addProperty("type", src?.javaClass?.simpleName) + when (src) { + is OfflineOperationType.CreateFolder -> { + jsonObject.addProperty("type", src.type) + jsonObject.addProperty("path", src.path) + } + + is OfflineOperationType.CreateFile -> { + jsonObject.addProperty("type", src.type) + jsonObject.addProperty("localPath", src.localPath) + jsonObject.addProperty("remotePath", src.remotePath) + jsonObject.addProperty("mimeType", src.mimeType) + } + + is OfflineOperationType.RenameFile -> { + jsonObject.addProperty("type", src.type) + jsonObject.addProperty("ocFileId", src.ocFileId) + jsonObject.addProperty("newName", src.newName) + } + + is OfflineOperationType.RemoveFile -> { + jsonObject.addProperty("type", src.type) + jsonObject.addProperty("path", src.path) + } + + null -> Unit + } + + return jsonObject + } + + override fun deserialize( + json: JsonElement?, + typeOfT: Type?, + context: JsonDeserializationContext? + ): OfflineOperationType? { + val jsonObject = json?.asJsonObject ?: return null + val type = jsonObject.get("type")?.asString + return when (type) { + OfflineOperationRawType.CreateFolder.name -> OfflineOperationType.CreateFolder( + jsonObject.get("type").asString, + jsonObject.get("path").asString + ) + + OfflineOperationRawType.CreateFile.name -> OfflineOperationType.CreateFile( + jsonObject.get("type").asString, + jsonObject.get("localPath").asString, + jsonObject.get("remotePath").asString, + jsonObject.get("mimeType").asString + ) + + OfflineOperationRawType.RenameFile.name -> OfflineOperationType.RenameFile( + jsonObject.get("type").asString, + jsonObject.get("ocFileId").asLong, + jsonObject.get("newName").asString + ) + + OfflineOperationRawType.RemoveFile.name -> OfflineOperationType.RemoveFile( + jsonObject.get("type").asString, + jsonObject.get("path").asString + ) + + else -> null + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/database/typeConverter/OfflineOperationTypeConverter.kt b/app/src/main/java/com/nextcloud/client/database/typeConverter/OfflineOperationTypeConverter.kt new file mode 100644 index 0000000..3436623 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/typeConverter/OfflineOperationTypeConverter.kt @@ -0,0 +1,30 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.typeConverter + +import androidx.room.ProvidedTypeConverter +import androidx.room.TypeConverter +import com.google.gson.Gson +import com.nextcloud.model.OfflineOperationType +import com.google.gson.GsonBuilder +import com.nextcloud.client.database.typeAdapter.OfflineOperationTypeAdapter + +@ProvidedTypeConverter +class OfflineOperationTypeConverter { + + private val gson: Gson = GsonBuilder() + .registerTypeAdapter(OfflineOperationType::class.java, OfflineOperationTypeAdapter()) + .create() + + @TypeConverter + fun fromOfflineOperationType(type: OfflineOperationType?): String? = gson.toJson(type) + + @TypeConverter + fun toOfflineOperationType(type: String?): OfflineOperationType? = + gson.fromJson(type, OfflineOperationType::class.java) +} diff --git a/app/src/main/java/com/nextcloud/client/device/BatteryStatus.kt b/app/src/main/java/com/nextcloud/client/device/BatteryStatus.kt index 8fee2a8..a941026 100644 --- a/app/src/main/java/com/nextcloud/client/device/BatteryStatus.kt +++ b/app/src/main/java/com/nextcloud/client/device/BatteryStatus.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.device diff --git a/app/src/main/java/com/nextcloud/client/device/DeviceInfo.kt b/app/src/main/java/com/nextcloud/client/device/DeviceInfo.kt index 6dc7273..4888930 100644 --- a/app/src/main/java/com/nextcloud/client/device/DeviceInfo.kt +++ b/app/src/main/java/com/nextcloud/client/device/DeviceInfo.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.device @@ -12,11 +12,10 @@ import android.os.Build import java.util.Locale class DeviceInfo { - val vendor: String = Build.MANUFACTURER.toLowerCase(Locale.ROOT) + val vendor: String = Build.MANUFACTURER.lowercase(Locale.ROOT) val apiLevel: Int = Build.VERSION.SDK_INT val androidVersion = Build.VERSION.RELEASE - fun hasCamera(context: Context): Boolean { - return context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) - } + fun hasCamera(context: Context): Boolean = + context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) } diff --git a/app/src/main/java/com/nextcloud/client/device/DeviceModule.kt b/app/src/main/java/com/nextcloud/client/device/DeviceModule.kt index 88fd1ee..74a99ef 100644 --- a/app/src/main/java/com/nextcloud/client/device/DeviceModule.kt +++ b/app/src/main/java/com/nextcloud/client/device/DeviceModule.kt @@ -2,9 +2,9 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky + * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.device diff --git a/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt b/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt index 663df6b..c627bb7 100644 --- a/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt +++ b/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.device diff --git a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt index 3a060cf..a6f2e91 100644 --- a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt +++ b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.device diff --git a/app/src/main/java/com/nextcloud/client/di/ActivityInjector.kt b/app/src/main/java/com/nextcloud/client/di/ActivityInjector.kt index a49c566..876c5d0 100644 --- a/app/src/main/java/com/nextcloud/client/di/ActivityInjector.kt +++ b/app/src/main/java/com/nextcloud/client/di/ActivityInjector.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di diff --git a/app/src/main/java/com/nextcloud/client/di/AppComponent.java b/app/src/main/java/com/nextcloud/client/di/AppComponent.java index 528ee63..462e257 100644 --- a/app/src/main/java/com/nextcloud/client/di/AppComponent.java +++ b/app/src/main/java/com/nextcloud/client/di/AppComponent.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; @@ -16,7 +16,10 @@ import com.nextcloud.client.device.DeviceModule; import com.nextcloud.client.integrations.IntegrationsModule; import com.nextcloud.client.jobs.JobsModule; import com.nextcloud.client.jobs.download.FileDownloadHelper; +import com.nextcloud.client.jobs.offlineOperations.receiver.OfflineOperationReceiver; +import com.nextcloud.client.jobs.upload.FileUploadBroadcastReceiver; import com.nextcloud.client.jobs.upload.FileUploadHelper; +import com.nextcloud.client.media.BackgroundPlayerService; import com.nextcloud.client.network.NetworkModule; import com.nextcloud.client.onboarding.OnboardingModule; import com.nextcloud.client.preferences.PreferencesModule; @@ -27,6 +30,8 @@ import com.owncloud.android.ui.whatsnew.ProgressIndicator; import javax.inject.Singleton; +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; import dagger.BindsInstance; import dagger.Component; import dagger.android.support.AndroidSupportInjectionModule; @@ -46,7 +51,7 @@ import dagger.android.support.AndroidSupportInjectionModule; ThemeModule.class, DatabaseModule.class, DispatcherModule.class, - VariantModule.class + VariantModule.class, }) @Singleton public interface AppComponent { @@ -55,6 +60,9 @@ public interface AppComponent { void inject(MediaControlView mediaControlView); + @OptIn(markerClass = UnstableApi.class) + void inject(BackgroundPlayerService backgroundPlayerService); + void inject(ThemeableSwitchPreference switchPreference); void inject(FileUploadHelper fileUploadHelper); @@ -63,6 +71,10 @@ public interface AppComponent { void inject(ProgressIndicator progressIndicator); + void inject(FileUploadBroadcastReceiver fileUploadBroadcastReceiver); + + void inject(OfflineOperationReceiver offlineOperationReceiver); + @Component.Builder interface Builder { @BindsInstance diff --git a/app/src/main/java/com/nextcloud/client/di/AppModule.java b/app/src/main/java/com/nextcloud/client/di/AppModule.java index eb79b07..32a0150 100644 --- a/app/src/main/java/com/nextcloud/client/di/AppModule.java +++ b/app/src/main/java/com/nextcloud/client/di/AppModule.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; @@ -28,6 +28,7 @@ import com.nextcloud.client.core.ClockImpl; import com.nextcloud.client.core.ThreadPoolAsyncRunner; import com.nextcloud.client.database.dao.ArbitraryDataDao; import com.nextcloud.client.device.DeviceInfo; +import com.nextcloud.client.jobs.operation.FileOperationHelper; import com.nextcloud.client.logger.FileLogHandler; import com.nextcloud.client.logger.Logger; import com.nextcloud.client.logger.LoggerImpl; @@ -55,6 +56,7 @@ import com.owncloud.android.ui.activities.data.activities.RemoteActivitiesReposi import com.owncloud.android.ui.activities.data.files.FilesRepository; import com.owncloud.android.ui.activities.data.files.FilesServiceApiImpl; import com.owncloud.android.ui.activities.data.files.RemoteFilesRepository; +import com.owncloud.android.ui.dialog.setupEncryption.CertificateValidator; import com.owncloud.android.utils.theme.ViewThemeUtils; import org.greenrobot.eventbus.EventBus; @@ -249,10 +251,21 @@ class AppModule { return new PassCodeManager(preferences, clock); } + @Provides + FileOperationHelper fileOperationHelper(CurrentAccountProvider currentAccountProvider, Context context) { + return new FileOperationHelper(currentAccountProvider.getUser(), context, fileDataStorageManager(currentAccountProvider, context)); + } + @Provides @Singleton UsersAndGroupsSearchConfig userAndGroupSearchConfig() { return new UsersAndGroupsSearchConfig(); } + + @Provides + @Singleton + CertificateValidator certificateValidator() { + return new CertificateValidator(); + } } diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java index 60346b2..dc7194f 100644 --- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -1,8 +1,9 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2024 TSI-mc * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; @@ -17,6 +18,7 @@ import com.nextcloud.client.jobs.transfer.FileTransferService; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.logger.ui.LogsActivity; import com.nextcloud.client.logger.ui.LogsViewModel; +import com.nextcloud.client.media.BackgroundPlayerService; import com.nextcloud.client.media.PlayerService; import com.nextcloud.client.migrations.Migrations; import com.nextcloud.client.onboarding.FirstRunActivity; @@ -24,11 +26,15 @@ import com.nextcloud.client.onboarding.WhatsNewActivity; import com.nextcloud.client.widget.DashboardWidgetConfigurationActivity; import com.nextcloud.client.widget.DashboardWidgetProvider; import com.nextcloud.client.widget.DashboardWidgetService; +import com.nextcloud.receiver.NetworkChangeReceiver; import com.nextcloud.ui.ChooseAccountDialogFragment; +import com.nextcloud.ui.ChooseStorageLocationDialogFragment; import com.nextcloud.ui.ImageDetailFragment; -import com.nextcloud.ui.SetStatusDialogFragment; +import com.nextcloud.ui.SetOnlineStatusBottomSheet; +import com.nextcloud.ui.SetStatusMessageBottomSheet; import com.nextcloud.ui.composeActivity.ComposeActivity; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; +import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet; import com.nmc.android.ui.LauncherActivity; import com.owncloud.android.MainApp; import com.owncloud.android.authentication.AuthenticatorActivity; @@ -54,6 +60,7 @@ import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.activity.FilePickerActivity; import com.owncloud.android.ui.activity.FolderPickerActivity; +import com.owncloud.android.ui.activity.InternalTwoWaySyncActivity; import com.owncloud.android.ui.activity.ManageAccountsActivity; import com.owncloud.android.ui.activity.ManageSpaceActivity; import com.owncloud.android.ui.activity.NotificationsActivity; @@ -83,16 +90,16 @@ import com.owncloud.android.ui.dialog.LocalStoragePathPickerDialogFragment; import com.owncloud.android.ui.dialog.MultipleAccountsDialog; import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment; import com.owncloud.android.ui.dialog.RenameFileDialogFragment; -import com.owncloud.android.ui.dialog.RenamePublicShareDialogFragment; import com.owncloud.android.ui.dialog.SendFilesDialog; import com.owncloud.android.ui.dialog.SendShareDialog; -import com.owncloud.android.ui.dialog.SetupEncryptionDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment; import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; +import com.owncloud.android.ui.dialog.TermsOfServiceDialog; +import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.fragment.ExtendedListFragment; import com.owncloud.android.ui.fragment.FeatureFragment; import com.owncloud.android.ui.fragment.FileDetailActivitiesFragment; @@ -121,6 +128,8 @@ import com.owncloud.android.ui.preview.PreviewTextStringFragment; import com.owncloud.android.ui.preview.pdf.PreviewPdfFragment; import com.owncloud.android.ui.trashbin.TrashbinActivity; +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; import dagger.Module; import dagger.android.ContributesAndroidInjector; @@ -219,6 +228,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract TrashbinActivity trashbinActivity(); + @ContributesAndroidInjector + abstract TrashbinFileActionsBottomSheet trashbinFileActionsBottomSheet(); + @ContributesAndroidInjector abstract UploadFilesActivity uploadFilesActivity(); @@ -289,7 +301,7 @@ abstract class ComponentsModule { abstract ChooseAccountDialogFragment chooseAccountDialogFragment(); @ContributesAndroidInjector - abstract SetStatusDialogFragment setStatusDialogFragment(); + abstract SetOnlineStatusBottomSheet setOnlineStatusBottomSheet(); @ContributesAndroidInjector abstract PreviewTextFileFragment previewTextFileFragment(); @@ -312,6 +324,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract BootupBroadcastReceiver bootupBroadcastReceiver(); + @ContributesAndroidInjector + abstract NetworkChangeReceiver networkChangeReceiver(); + @ContributesAndroidInjector abstract NotificationWork.NotificationReceiver notificationWorkBroadcastReceiver(); @@ -399,15 +414,15 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract RemoveFilesDialogFragment removeFilesDialogFragment(); - @ContributesAndroidInjector - abstract RenamePublicShareDialogFragment renamePublicShareDialogFragment(); - @ContributesAndroidInjector abstract SendShareDialog sendShareDialog(); @ContributesAndroidInjector abstract SetupEncryptionDialogFragment setupEncryptionDialogFragment(); + @ContributesAndroidInjector + abstract ChooseStorageLocationDialogFragment chooseStorageLocationDialogFragment(); + @ContributesAndroidInjector abstract SharePasswordDialogFragment sharePasswordDialogFragment(); @@ -476,4 +491,18 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract TestJob testJob(); + + @ContributesAndroidInjector + abstract InternalTwoWaySyncActivity internalTwoWaySyncActivity(); + + + @OptIn(markerClass = UnstableApi.class) + @ContributesAndroidInjector + abstract BackgroundPlayerService backgroundPlayerService(); + + @ContributesAndroidInjector + abstract TermsOfServiceDialog termsOfServiceDialog(); + + @ContributesAndroidInjector + abstract SetStatusMessageBottomSheet setStatusMessageBottomSheet(); } diff --git a/app/src/main/java/com/nextcloud/client/di/DispatcherModule.kt b/app/src/main/java/com/nextcloud/client/di/DispatcherModule.kt index 7f2679b..ab15cdf 100644 --- a/app/src/main/java/com/nextcloud/client/di/DispatcherModule.kt +++ b/app/src/main/java/com/nextcloud/client/di/DispatcherModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di diff --git a/app/src/main/java/com/nextcloud/client/di/FragmentInjector.kt b/app/src/main/java/com/nextcloud/client/di/FragmentInjector.kt index 4622095..5d76e73 100644 --- a/app/src/main/java/com/nextcloud/client/di/FragmentInjector.kt +++ b/app/src/main/java/com/nextcloud/client/di/FragmentInjector.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di @@ -12,11 +12,7 @@ import androidx.fragment.app.FragmentManager import dagger.android.support.AndroidSupportInjection internal class FragmentInjector : FragmentManager.FragmentLifecycleCallbacks() { - override fun onFragmentPreAttached( - fragmentManager: FragmentManager, - fragment: Fragment, - context: Context - ) { + override fun onFragmentPreAttached(fragmentManager: FragmentManager, fragment: Fragment, context: Context) { super.onFragmentPreAttached(fragmentManager, fragment, context) if (fragment is Injectable) { try { diff --git a/app/src/main/java/com/nextcloud/client/di/Injectable.java b/app/src/main/java/com/nextcloud/client/di/Injectable.java index 28ab315..2edc275 100644 --- a/app/src/main/java/com/nextcloud/client/di/Injectable.java +++ b/app/src/main/java/com/nextcloud/client/di/Injectable.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; diff --git a/app/src/main/java/com/nextcloud/client/di/InjectorNotFoundException.java b/app/src/main/java/com/nextcloud/client/di/InjectorNotFoundException.java index 398f4db..7c802f4 100644 --- a/app/src/main/java/com/nextcloud/client/di/InjectorNotFoundException.java +++ b/app/src/main/java/com/nextcloud/client/di/InjectorNotFoundException.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; diff --git a/app/src/main/java/com/nextcloud/client/di/ThemeModule.kt b/app/src/main/java/com/nextcloud/client/di/ThemeModule.kt index 758060b..99a32ad 100644 --- a/app/src/main/java/com/nextcloud/client/di/ThemeModule.kt +++ b/app/src/main/java/com/nextcloud/client/di/ThemeModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di @@ -27,19 +27,14 @@ internal abstract class ThemeModule { @Provides @Singleton - fun themeColorUtils(): ThemeColorUtils { - return ThemeColorUtils() - } + fun themeColorUtils(): ThemeColorUtils = ThemeColorUtils() @Provides @Singleton - fun themeUtils(): ThemeUtils { - return ThemeUtils() - } + fun themeUtils(): ThemeUtils = ThemeUtils() @Provides - fun provideMaterialSchemes(materialSchemesProvider: MaterialSchemesProvider): MaterialSchemes { - return materialSchemesProvider.getMaterialSchemesForCurrentUser() - } + fun provideMaterialSchemes(materialSchemesProvider: MaterialSchemesProvider): MaterialSchemes = + materialSchemesProvider.getMaterialSchemesForCurrentUser() } } diff --git a/app/src/main/java/com/nextcloud/client/di/ViewModelFactory.kt b/app/src/main/java/com/nextcloud/client/di/ViewModelFactory.kt index 3b1b6b0..08041ae 100644 --- a/app/src/main/java/com/nextcloud/client/di/ViewModelFactory.kt +++ b/app/src/main/java/com/nextcloud/client/di/ViewModelFactory.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di diff --git a/app/src/main/java/com/nextcloud/client/di/ViewModelKey.kt b/app/src/main/java/com/nextcloud/client/di/ViewModelKey.kt index 0d83151..baf2676 100644 --- a/app/src/main/java/com/nextcloud/client/di/ViewModelKey.kt +++ b/app/src/main/java/com/nextcloud/client/di/ViewModelKey.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di diff --git a/app/src/main/java/com/nextcloud/client/di/ViewModelModule.kt b/app/src/main/java/com/nextcloud/client/di/ViewModelModule.kt index 5fd38ab..eb3e98a 100644 --- a/app/src/main/java/com/nextcloud/client/di/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/client/di/ViewModelModule.kt @@ -1,8 +1,9 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2024 TSI-mc * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di @@ -13,6 +14,7 @@ import com.nextcloud.client.etm.EtmViewModel import com.nextcloud.client.logger.ui.LogsViewModel import com.nextcloud.ui.fileactions.FileActionsViewModel import com.owncloud.android.ui.preview.pdf.PreviewPdfViewModel +import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsViewModel import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel import dagger.Binds import dagger.Module @@ -50,6 +52,11 @@ abstract class ViewModelModule { @ViewModelKey(DocumentScanViewModel::class) abstract fun documentScanViewModel(vm: DocumentScanViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(TrashbinFileActionsViewModel::class) + abstract fun trashbinFileActionsViewModel(vm: TrashbinFileActionsViewModel): ViewModel + @Binds abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory } diff --git a/app/src/main/java/com/nextcloud/client/di/package-info.java b/app/src/main/java/com/nextcloud/client/di/package-info.java index 2d6cb6d..e7e0175 100644 --- a/app/src/main/java/com/nextcloud/client/di/package-info.java +++ b/app/src/main/java/com/nextcloud/client/di/package-info.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ /** diff --git a/app/src/main/java/com/nextcloud/client/documentscan/AppScanOptionalFeature.kt b/app/src/main/java/com/nextcloud/client/documentscan/AppScanOptionalFeature.kt index e2b4505..d6996e1 100644 --- a/app/src/main/java/com/nextcloud/client/documentscan/AppScanOptionalFeature.kt +++ b/app/src/main/java/com/nextcloud/client/documentscan/AppScanOptionalFeature.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.documentscan @@ -21,9 +21,8 @@ abstract class AppScanOptionalFeature { */ @Suppress("unused") // used only in some variants object Stub : AppScanOptionalFeature() { - override fun getScanContract(): ActivityResultContract { + override fun getScanContract(): ActivityResultContract = throw UnsupportedOperationException("Document scan is not available") - } override val isAvailable = false } diff --git a/app/src/main/java/com/nextcloud/client/documentscan/DocumentPageListAdapter.kt b/app/src/main/java/com/nextcloud/client/documentscan/DocumentPageListAdapter.kt index 0d8e7de..17ff411 100644 --- a/app/src/main/java/com/nextcloud/client/documentscan/DocumentPageListAdapter.kt +++ b/app/src/main/java/com/nextcloud/client/documentscan/DocumentPageListAdapter.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.documentscan @@ -28,9 +28,7 @@ class DocumentPageListAdapter : holder.bind(currentList[position]) } - override fun getItemCount(): Int { - return currentList.size - } + override fun getItemCount(): Int = currentList.size class DocumentPageViewHolder(val binding: DocumentPageItemBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(imagePath: String) { @@ -39,10 +37,8 @@ class DocumentPageListAdapter : } private class DiffItemCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: String, newItem: String) = - oldItem == newItem + override fun areItemsTheSame(oldItem: String, newItem: String) = oldItem == newItem - override fun areContentsTheSame(oldItem: String, newItem: String) = - oldItem == newItem + override fun areContentsTheSame(oldItem: String, newItem: String) = oldItem == newItem } } diff --git a/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanActivity.kt b/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanActivity.kt index 349152f..241b0db 100644 --- a/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanActivity.kt +++ b/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.documentscan @@ -27,7 +27,9 @@ import com.owncloud.android.ui.activity.ToolbarActivity import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject -class DocumentScanActivity : ToolbarActivity(), Injectable { +class DocumentScanActivity : + ToolbarActivity(), + Injectable { @Inject lateinit var vmFactory: ViewModelFactory @@ -96,18 +98,16 @@ class DocumentScanActivity : ToolbarActivity(), Injectable { } } - override fun onMenuItemSelected(menuItem: MenuItem): Boolean { - return when (menuItem.itemId) { - R.id.action_save -> { - viewModel.onClickDone() - true - } - android.R.id.home -> { - onBackPressed() - true - } - else -> false + override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { + R.id.action_save -> { + viewModel.onClickDone() + true } + android.R.id.home -> { + onBackPressed() + true + } + else -> false } } ) diff --git a/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanViewModel.kt b/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanViewModel.kt index b74d7b3..e7cc433 100644 --- a/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/documentscan/DocumentScanViewModel.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.documentscan @@ -46,15 +46,11 @@ class DocumentScanViewModel @Inject constructor( get() = pageList.isEmpty() } - class NormalState( - pageList: List = emptyList(), - val shouldRequestScan: Boolean = false - ) : BaseState(pageList) + class NormalState(pageList: List = emptyList(), val shouldRequestScan: Boolean = false) : + BaseState(pageList) - class RequestExportState( - pageList: List = emptyList(), - val shouldRequestExportType: Boolean = true - ) : BaseState(pageList) + class RequestExportState(pageList: List = emptyList(), val shouldRequestExportType: Boolean = true) : + BaseState(pageList) object DoneState : UIState object CanceledState : UIState diff --git a/app/src/main/java/com/nextcloud/client/documentscan/GeneratePDFUseCase.kt b/app/src/main/java/com/nextcloud/client/documentscan/GeneratePDFUseCase.kt index b25d819..0f6cfa2 100644 --- a/app/src/main/java/com/nextcloud/client/documentscan/GeneratePDFUseCase.kt +++ b/app/src/main/java/com/nextcloud/client/documentscan/GeneratePDFUseCase.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.documentscan @@ -22,40 +22,30 @@ class GeneratePDFUseCase @Inject constructor(private val logger: Logger) { * @param imagePaths list of image paths * @return `true` if the PDF was generated successfully, `false` otherwise */ - fun execute(imagePaths: List, filePath: String): Boolean { - return if (imagePaths.isEmpty() || filePath.isBlank()) { - logger.w(TAG, "Invalid parameters: imagePaths: $imagePaths, filePath: $filePath") - false - } else { - val document = PdfDocument() - fillDocumentPages(document, imagePaths) - writePdfToFile(filePath, document) - } + fun execute(imagePaths: List, filePath: String): Boolean = if (imagePaths.isEmpty() || filePath.isBlank()) { + logger.w(TAG, "Invalid parameters: imagePaths: $imagePaths, filePath: $filePath") + false + } else { + val document = PdfDocument() + fillDocumentPages(document, imagePaths) + writePdfToFile(filePath, document) } /** * @return `true` if the PDF was generated successfully, `false` otherwise */ - private fun writePdfToFile( - filePath: String, - document: PdfDocument - ): Boolean { - return try { - val fileOutputStream = FileOutputStream(filePath) - document.writeTo(fileOutputStream) - fileOutputStream.close() - document.close() - true - } catch (ex: IOException) { - logger.e(TAG, "Error generating PDF", ex) - false - } + private fun writePdfToFile(filePath: String, document: PdfDocument): Boolean = try { + val fileOutputStream = FileOutputStream(filePath) + document.writeTo(fileOutputStream) + fileOutputStream.close() + document.close() + true + } catch (ex: IOException) { + logger.e(TAG, "Error generating PDF", ex) + false } - private fun fillDocumentPages( - document: PdfDocument, - imagePaths: List - ) { + private fun fillDocumentPages(document: PdfDocument, imagePaths: List) { imagePaths.forEach { path -> val bitmap = BitmapFactory.decodeFile(path) val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, 1).create() diff --git a/app/src/main/java/com/nextcloud/client/documentscan/GeneratePdfFromImagesWork.kt b/app/src/main/java/com/nextcloud/client/documentscan/GeneratePdfFromImagesWork.kt index f56b1b8..c1115de 100644 --- a/app/src/main/java/com/nextcloud/client/documentscan/GeneratePdfFromImagesWork.kt +++ b/app/src/main/java/com/nextcloud/client/documentscan/GeneratePdfFromImagesWork.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.documentscan @@ -112,7 +112,8 @@ class GeneratePdfFromImagesWork( user, arrayOf(pdfPath), arrayOf(uploadPath), - FileUploadWorker.LOCAL_BEHAVIOUR_DELETE, // MIME type will be detected from file name + // MIME type will be detected from file name + FileUploadWorker.LOCAL_BEHAVIOUR_DELETE, true, UploadFileOperation.CREATED_BY_USER, false, diff --git a/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt b/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt index b16e0f6..795f974 100644 --- a/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt +++ b/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 ZetaTom * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.editimage @@ -15,7 +15,6 @@ import android.view.Menu import android.view.MenuItem import android.view.View import androidx.appcompat.content.res.AppCompatResources -import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -59,9 +58,7 @@ class EditImageActivity : MimeType.HEIC ) - fun canBePreviewed(file: OCFile): Boolean { - return file.mimeType in supportedMimeTypes - } + fun canBePreviewed(file: OCFile): Boolean = file.mimeType in supportedMimeTypes } override fun onCreate(savedInstanceState: Bundle?) { @@ -82,7 +79,6 @@ class EditImageActivity : val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView) windowInsetsController.hide(WindowInsetsCompat.Type.statusBars()) - window.statusBarColor = ContextCompat.getColor(this, R.color.black) window.navigationBarColor = getColor(R.color.black) setupCropper() @@ -129,9 +125,7 @@ class EditImageActivity : } menu?.findItem(R.id.custom_menu_placeholder_item)?.apply { icon = saveIcon - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - contentDescription = getString(R.string.common_save) - } + contentDescription = getString(R.string.common_save) } return true } diff --git a/app/src/main/java/com/nextcloud/client/errorhandling/ExceptionHandler.kt b/app/src/main/java/com/nextcloud/client/errorhandling/ExceptionHandler.kt index 5da2509..f68f175 100644 --- a/app/src/main/java/com/nextcloud/client/errorhandling/ExceptionHandler.kt +++ b/app/src/main/java/com/nextcloud/client/errorhandling/ExceptionHandler.kt @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2019 Andy Scherzinger * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2014 Luke Owncloud - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.nextcloud.client.errorhandling diff --git a/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt b/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt index c8d1be3..f9e4b12 100644 --- a/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt +++ b/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.errorhandling @@ -17,7 +17,6 @@ import com.owncloud.android.R import com.owncloud.android.databinding.ActivityShowErrorBinding import com.owncloud.android.utils.ClipboardUtil import com.owncloud.android.utils.DisplayUtils -import java.net.URLEncoder class ShowErrorActivity : AppCompatActivity() { private lateinit var binding: ActivityShowErrorBinding @@ -51,10 +50,7 @@ class ShowErrorActivity : AppCompatActivity() { private fun reportIssue() { ClipboardUtil.copyToClipboard(this, binding.textViewError.text.toString(), false) - val issueLink = String.format( - getString(R.string.report_issue_link), - URLEncoder.encode(binding.textViewError.text.toString(), Charsets.UTF_8.name()) - ) + val issueLink = getString(R.string.report_issue_link) DisplayUtils.startLinkIntent(this, issueLink) Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_LONG).show() } @@ -64,14 +60,12 @@ class ShowErrorActivity : AppCompatActivity() { return super.onCreateOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.error_share -> { - onClickedShare() - true - } - else -> super.onOptionsItemSelected(item) + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.error_share -> { + onClickedShare() + true } + else -> super.onOptionsItemSelected(item) } private fun onClickedShare() { diff --git a/app/src/main/java/com/nextcloud/client/etm/EtmActivity.kt b/app/src/main/java/com/nextcloud/client/etm/EtmActivity.kt index 6052dc1..9d5b72a 100644 --- a/app/src/main/java/com/nextcloud/client/etm/EtmActivity.kt +++ b/app/src/main/java/com/nextcloud/client/etm/EtmActivity.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm @@ -18,7 +18,9 @@ import com.owncloud.android.R import com.owncloud.android.ui.activity.ToolbarActivity import javax.inject.Inject -class EtmActivity : ToolbarActivity(), Injectable { +class EtmActivity : + ToolbarActivity(), + Injectable { companion object { @JvmStatic @@ -46,16 +48,14 @@ class EtmActivity : ToolbarActivity(), Injectable { ) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - if (!vm.onBackPressed()) { - finish() - } - true + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + android.R.id.home -> { + if (!vm.onBackPressed()) { + finish() } - else -> super.onOptionsItemSelected(item) + true } + else -> super.onOptionsItemSelected(item) } @Deprecated("Deprecated in Java") diff --git a/app/src/main/java/com/nextcloud/client/etm/EtmBaseFragment.kt b/app/src/main/java/com/nextcloud/client/etm/EtmBaseFragment.kt index a92bb5a..aac5488 100644 --- a/app/src/main/java/com/nextcloud/client/etm/EtmBaseFragment.kt +++ b/app/src/main/java/com/nextcloud/client/etm/EtmBaseFragment.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm diff --git a/app/src/main/java/com/nextcloud/client/etm/EtmMenuAdapter.kt b/app/src/main/java/com/nextcloud/client/etm/EtmMenuAdapter.kt index f8aea79..bd0937b 100644 --- a/app/src/main/java/com/nextcloud/client/etm/EtmMenuAdapter.kt +++ b/app/src/main/java/com/nextcloud/client/etm/EtmMenuAdapter.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm @@ -15,10 +15,8 @@ import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.R -class EtmMenuAdapter( - context: Context, - val onItemClicked: (Int) -> Unit -) : RecyclerView.Adapter() { +class EtmMenuAdapter(context: Context, val onItemClicked: (Int) -> Unit) : + RecyclerView.Adapter() { private val layoutInflater = LayoutInflater.from(context) var pages: List = listOf() @@ -49,7 +47,5 @@ class EtmMenuAdapter( holder.secondaryAction.setImageResource(0) } - override fun getItemCount(): Int { - return pages.size - } + override fun getItemCount(): Int = pages.size } diff --git a/app/src/main/java/com/nextcloud/client/etm/EtmMenuEntry.kt b/app/src/main/java/com/nextcloud/client/etm/EtmMenuEntry.kt index 09ad91d..6d4ff08 100644 --- a/app/src/main/java/com/nextcloud/client/etm/EtmMenuEntry.kt +++ b/app/src/main/java/com/nextcloud/client/etm/EtmMenuEntry.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm diff --git a/app/src/main/java/com/nextcloud/client/etm/EtmMenuFragment.kt b/app/src/main/java/com/nextcloud/client/etm/EtmMenuFragment.kt index c6bb397..ace0f99 100644 --- a/app/src/main/java/com/nextcloud/client/etm/EtmMenuFragment.kt +++ b/app/src/main/java/com/nextcloud/client/etm/EtmMenuFragment.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm diff --git a/app/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt b/app/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt index c40b30e..6007973 100644 --- a/app/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm @@ -76,7 +76,7 @@ class EtmViewModel @Inject constructor( pageClass = EtmPreferencesFragment::class ), EtmMenuEntry( - iconRes = R.drawable.ic_user, + iconRes = R.drawable.ic_user_outline, titleRes = R.string.etm_accounts, pageClass = EtmAccountsFragment::class ), diff --git a/app/src/main/java/com/nextcloud/client/etm/pages/EtmAccountsFragment.kt b/app/src/main/java/com/nextcloud/client/etm/pages/EtmAccountsFragment.kt index be6e1e2..b05e7c4 100644 --- a/app/src/main/java/com/nextcloud/client/etm/pages/EtmAccountsFragment.kt +++ b/app/src/main/java/com/nextcloud/client/etm/pages/EtmAccountsFragment.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm.pages @@ -20,7 +20,7 @@ import com.owncloud.android.databinding.FragmentEtmAccountsBinding class EtmAccountsFragment : EtmBaseFragment() { private var _binding: FragmentEtmAccountsBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -50,14 +50,12 @@ class EtmAccountsFragment : EtmBaseFragment() { inflater.inflate(R.menu.fragment_etm_accounts, menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.etm_accounts_share -> { - onClickedShare() - true - } - else -> super.onOptionsItemSelected(item) + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.etm_accounts_share -> { + onClickedShare() + true } + else -> super.onOptionsItemSelected(item) } private fun onClickedShare() { diff --git a/app/src/main/java/com/nextcloud/client/etm/pages/EtmBackgroundJobsFragment.kt b/app/src/main/java/com/nextcloud/client/etm/pages/EtmBackgroundJobsFragment.kt index a335059..a86dc38 100644 --- a/app/src/main/java/com/nextcloud/client/etm/pages/EtmBackgroundJobsFragment.kt +++ b/app/src/main/java/com/nextcloud/client/etm/pages/EtmBackgroundJobsFragment.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm.pages @@ -16,6 +16,7 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.core.view.isVisible import androidx.lifecycle.Observer import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager @@ -30,7 +31,9 @@ import java.text.SimpleDateFormat import java.util.Locale import javax.inject.Inject -class EtmBackgroundJobsFragment : EtmBaseFragment(), Injectable { +class EtmBackgroundJobsFragment : + EtmBaseFragment(), + Injectable { @Inject lateinit var preferences: AppPreferences @@ -51,9 +54,9 @@ class EtmBackgroundJobsFragment : EtmBaseFragment(), Injectable { private val executionLogRow = view.findViewById(R.id.etm_background_execution_logs_row) val executionTimesRow = view.findViewById(R.id.etm_background_execution_times_row) - var progressEnabled: Boolean = progressRow.visibility == View.VISIBLE + var progressEnabled: Boolean = progressRow.isVisible get() { - return progressRow.visibility == View.VISIBLE + return progressRow.isVisible } set(value) { field = value @@ -64,9 +67,9 @@ class EtmBackgroundJobsFragment : EtmBaseFragment(), Injectable { } } - var logsEnabled: Boolean = executionLogRow.visibility == View.VISIBLE + var logsEnabled: Boolean = executionLogRow.isVisible get() { - return executionLogRow.visibility == View.VISIBLE + return executionLogRow.isVisible } set(value) { field = value @@ -80,6 +83,7 @@ class EtmBackgroundJobsFragment : EtmBaseFragment(), Injectable { private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:MM:ssZ", Locale.getDefault()) var backgroundJobs: List = emptyList() + @SuppressLint("NotifyDataSetChanged") set(value) { field = value notifyDataSetChanged() @@ -96,9 +100,7 @@ class EtmBackgroundJobsFragment : EtmBaseFragment(), Injectable { return viewHolder } - override fun getItemCount(): Int { - return backgroundJobs.size - } + override fun getItemCount(): Int = backgroundJobs.size @SuppressLint("SetTextI18n") override fun onBindViewHolder(vh: ViewHolder, position: Int) { @@ -164,40 +166,40 @@ class EtmBackgroundJobsFragment : EtmBaseFragment(), Injectable { return view } + @Deprecated("Deprecated in Java") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.fragment_etm_background_jobs, menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.etm_background_jobs_cancel -> { - vm.cancelAllJobs() - true - } - - R.id.etm_background_jobs_prune -> { - vm.pruneJobs() - true - } - - R.id.etm_background_jobs_start_test -> { - vm.startTestJob(periodic = false) - true - } - - R.id.etm_background_jobs_schedule_test -> { - vm.startTestJob(periodic = true) - true - } - - R.id.etm_background_jobs_cancel_test -> { - vm.cancelTestJob() - true - } - - else -> super.onOptionsItemSelected(item) + @Deprecated("Deprecated in Java") + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.etm_background_jobs_cancel -> { + vm.cancelAllJobs() + true } + + R.id.etm_background_jobs_prune -> { + vm.pruneJobs() + true + } + + R.id.etm_background_jobs_start_test -> { + vm.startTestJob(periodic = false) + true + } + + R.id.etm_background_jobs_schedule_test -> { + vm.startTestJob(periodic = true) + true + } + + R.id.etm_background_jobs_cancel_test -> { + vm.cancelTestJob() + true + } + + else -> super.onOptionsItemSelected(item) } private fun onBackgroundJobsUpdated(backgroundJobs: List) { diff --git a/app/src/main/java/com/nextcloud/client/etm/pages/EtmFileTransferFragment.kt b/app/src/main/java/com/nextcloud/client/etm/pages/EtmFileTransferFragment.kt index 07c0fde..0e72ba8 100644 --- a/app/src/main/java/com/nextcloud/client/etm/pages/EtmFileTransferFragment.kt +++ b/app/src/main/java/com/nextcloud/client/etm/pages/EtmFileTransferFragment.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm.pages @@ -15,6 +15,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import androidx.core.view.isVisible import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -26,6 +27,7 @@ import com.nextcloud.client.jobs.transfer.TransferManager import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile import com.owncloud.android.db.OCUpload +import java.util.Locale class EtmFileTransferFragment : EtmBaseFragment() { @@ -45,9 +47,9 @@ class EtmFileTransferFragment : EtmBaseFragment() { val progress = view.findViewById(R.id.etm_transfer_progress) private val progressRow = view.findViewById(R.id.etm_transfer_progress_row) - var progressEnabled: Boolean = progressRow.visibility == View.VISIBLE + var progressEnabled: Boolean = progressRow.isVisible get() { - return progressRow.visibility == View.VISIBLE + return progressRow.isVisible } set(value) { field = value @@ -71,9 +73,7 @@ class EtmFileTransferFragment : EtmBaseFragment() { return ViewHolder(view) } - override fun getItemCount(): Int { - return transfers.size - } + override fun getItemCount(): Int = transfers.size override fun onBindViewHolder(vh: ViewHolder, position: Int) { val transfer = transfers[position] @@ -96,7 +96,7 @@ class EtmFileTransferFragment : EtmBaseFragment() { vh.state.text = transfer.state.toString() if (transfer.progress >= 0) { vh.progressEnabled = true - vh.progress.text = transfer.progress.toString() + vh.progress.text = String.format(Locale.getDefault(), "%d", transfer.progress) } else { vh.progressEnabled = false } @@ -137,18 +137,16 @@ class EtmFileTransferFragment : EtmBaseFragment() { inflater.inflate(R.menu.fragment_etm_file_transfer, menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.etm_test_download -> { - scheduleTestDownload() - true - } - R.id.etm_test_upload -> { - scheduleTestUpload() - true - } - else -> super.onOptionsItemSelected(item) + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.etm_test_download -> { + scheduleTestDownload() + true } + R.id.etm_test_upload -> { + scheduleTestUpload() + true + } + else -> super.onOptionsItemSelected(item) } private fun scheduleTestDownload() { diff --git a/app/src/main/java/com/nextcloud/client/etm/pages/EtmMigrations.kt b/app/src/main/java/com/nextcloud/client/etm/pages/EtmMigrations.kt index 4fe2652..3fd58f5 100644 --- a/app/src/main/java/com/nextcloud/client/etm/pages/EtmMigrations.kt +++ b/app/src/main/java/com/nextcloud/client/etm/pages/EtmMigrations.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm.pages @@ -20,7 +20,7 @@ import java.util.Locale class EtmMigrations : EtmBaseFragment() { private var _binding: FragmentEtmMigrationsBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -40,7 +40,7 @@ class EtmMigrations : EtmBaseFragment() { fun showStatus() { val builder = StringBuilder() - val status = vm.migrationsStatus.toString().toLowerCase(Locale.US) + val status = vm.migrationsStatus.toString().lowercase(Locale.US) builder.append("Migration status: $status\n") val lastMigratedVersion = if (vm.lastMigratedVersion >= 0) { vm.lastMigratedVersion.toString() @@ -65,14 +65,12 @@ class EtmMigrations : EtmBaseFragment() { inflater.inflate(R.menu.fragment_etm_migrations, menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.etm_migrations_delete -> { - onDeleteMigrationsClicked() - true - } - else -> super.onOptionsItemSelected(item) + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.etm_migrations_delete -> { + onDeleteMigrationsClicked() + true } + else -> super.onOptionsItemSelected(item) } private fun onDeleteMigrationsClicked() { diff --git a/app/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt b/app/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt index 7513902..e10b29f 100644 --- a/app/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt +++ b/app/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm.pages @@ -20,7 +20,7 @@ import com.owncloud.android.databinding.FragmentEtmPreferencesBinding class EtmPreferencesFragment : EtmBaseFragment() { private var _binding: FragmentEtmPreferencesBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -45,14 +45,12 @@ class EtmPreferencesFragment : EtmBaseFragment() { inflater.inflate(R.menu.fragment_etm_preferences, menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.etm_preferences_share -> { - onClickedShare() - true - } - else -> super.onOptionsItemSelected(item) + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.etm_preferences_share -> { + onClickedShare() + true } + else -> super.onOptionsItemSelected(item) } private fun onClickedShare() { diff --git a/app/src/main/java/com/nextcloud/client/files/DeepLinkConstants.kt b/app/src/main/java/com/nextcloud/client/files/DeepLinkConstants.kt new file mode 100644 index 0000000..0ad9842 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/files/DeepLinkConstants.kt @@ -0,0 +1,31 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.files + +import com.owncloud.android.R + +enum class DeepLinkConstants(val route: String, val navId: Int) { + OPEN_FILES("openFiles", R.id.nav_all_files), + OPEN_FAVORITES("openFavorites", R.id.nav_favorites), + OPEN_MEDIA("openMedia", R.id.nav_gallery), + OPEN_SHARED("openShared", R.id.nav_shared), + OPEN_OFFLINE("openOffline", R.id.nav_on_device), + OPEN_NOTIFICATIONS("openNotifications", -1), + OPEN_DELETED("openDeleted", R.id.nav_trashbin), + OPEN_SETTINGS("openSettings", R.id.nav_settings), + + // Special case, handled separately + OPEN_AUTO_UPLOAD("openAutoUpload", -1), + OPEN_EXTERNAL_URL("openUrl", -1), + ACTION_CREATE_NEW("createNew", -1), + ACTION_APP_UPDATE("checkAppUpdate", -1); + + companion object { + fun fromPath(path: String?): DeepLinkConstants? = entries.find { it.route == path } + } +} diff --git a/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt b/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt index c4707f9..a7cc811 100644 --- a/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt +++ b/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files @@ -18,9 +18,7 @@ import com.nextcloud.client.account.UserAccountManager * [com.nextcloud.client.mixins.ActivityMixin] and handle UI callbacks as well */ @Suppress("ForbiddenComment") -class DeepLinkHandler( - private val userAccountManager: UserAccountManager -) { +class DeepLinkHandler(private val userAccountManager: UserAccountManager) { /** * Provide parsed link arguments and context information required @@ -30,9 +28,9 @@ class DeepLinkHandler( companion object { val DEEP_LINK_PATTERN = Regex("""(.*?)(/index\.php)?/f/([0-9]+)$""") - val BASE_URL_GROUP_INDEX = 1 - val INDEX_PATH_GROUP_INDEX = 2 - val FILE_ID_GROUP_INDEX = 3 + const val BASE_URL_GROUP_INDEX = 1 + const val INDEX_PATH_GROUP_INDEX = 2 + const val FILE_ID_GROUP_INDEX = 3 } /** diff --git a/app/src/main/java/com/nextcloud/client/files/Direction.kt b/app/src/main/java/com/nextcloud/client/files/Direction.kt index e2f0402..d84987a 100644 --- a/app/src/main/java/com/nextcloud/client/files/Direction.kt +++ b/app/src/main/java/com/nextcloud/client/files/Direction.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files diff --git a/app/src/main/java/com/nextcloud/client/files/Registry.kt b/app/src/main/java/com/nextcloud/client/files/Registry.kt index 29bb6e0..9e75643 100644 --- a/app/src/main/java/com/nextcloud/client/files/Registry.kt +++ b/app/src/main/java/com/nextcloud/client/files/Registry.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files @@ -144,7 +144,5 @@ internal class Registry( * @param id transfer id * @return transfer status if found, null otherwise */ - fun getTransfer(uuid: UUID): Transfer? { - return pendingQueue[uuid] ?: runningQueue[uuid] ?: completedQueue[uuid] - } + fun getTransfer(uuid: UUID): Transfer? = pendingQueue[uuid] ?: runningQueue[uuid] ?: completedQueue[uuid] } diff --git a/app/src/main/java/com/nextcloud/client/files/Request.kt b/app/src/main/java/com/nextcloud/client/files/Request.kt index 61b1071..cc72bce 100644 --- a/app/src/main/java/com/nextcloud/client/files/Request.kt +++ b/app/src/main/java/com/nextcloud/client/files/Request.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files @@ -18,13 +18,8 @@ import com.owncloud.android.db.OCUpload import com.owncloud.android.files.services.NameCollisionPolicy import java.util.UUID -sealed class Request( - val user: User, - val file: OCFile, - val uuid: UUID, - val type: Direction, - val test: Boolean -) : Parcelable +sealed class Request(val user: User, val file: OCFile, val uuid: UUID, val type: Direction, val test: Boolean) : + Parcelable /** * Transfer request. This class should collect all information @@ -74,18 +69,12 @@ class DownloadRequest internal constructor( parcel.writeInt(if (test) 1 else 0) } - override fun describeContents(): Int { - return 0 - } + override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): DownloadRequest { - return DownloadRequest(parcel) - } + override fun createFromParcel(parcel: Parcel): DownloadRequest = DownloadRequest(parcel) - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } + override fun newArray(size: Int): Array = arrayOfNulls(size) } } @@ -138,18 +127,12 @@ class UploadRequest internal constructor( parcel.writeInt(if (test) 1 else 0) } - override fun describeContents(): Int { - return 0 - } + override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): UploadRequest { - return UploadRequest(parcel) - } + override fun createFromParcel(parcel: Parcel): UploadRequest = UploadRequest(parcel) - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } + override fun newArray(size: Int): Array = arrayOfNulls(size) } /** diff --git a/app/src/main/java/com/nextcloud/client/integrations/IntegrationsModule.kt b/app/src/main/java/com/nextcloud/client/integrations/IntegrationsModule.kt index 6a88e5d..9a77053 100644 --- a/app/src/main/java/com/nextcloud/client/integrations/IntegrationsModule.kt +++ b/app/src/main/java/com/nextcloud/client/integrations/IntegrationsModule.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.integrations @@ -16,7 +16,5 @@ import dagger.Provides @Module class IntegrationsModule { @Provides - fun deckApi(context: Context, packageManager: PackageManager): DeckApi { - return DeckApiImpl(context, packageManager) - } + fun deckApi(context: Context, packageManager: PackageManager): DeckApi = DeckApiImpl(context, packageManager) } diff --git a/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApi.kt b/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApi.kt index 1880ce8..b956e4c 100644 --- a/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApi.kt +++ b/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApi.kt @@ -25,8 +25,5 @@ interface DeckApi { * value otherwise * @see [Deck Server App](https://apps.nextcloud.com/apps/deck) */ - fun createForwardToDeckActionIntent( - notification: Notification, - user: User - ): Optional + fun createForwardToDeckActionIntent(notification: Notification, user: User): Optional } diff --git a/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApiImpl.kt b/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApiImpl.kt index 2b9404a..82b1394 100644 --- a/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApiImpl.kt +++ b/app/src/main/java/com/nextcloud/client/integrations/deck/DeckApiImpl.kt @@ -38,18 +38,16 @@ class DeckApiImpl(private val context: Context, private val packageManager: Pack ) } - private fun putExtrasToIntent(intent: Intent, notification: Notification, user: User): Intent { - return intent - .putExtra(EXTRA_ACCOUNT, user.accountName) - .putExtra(EXTRA_LINK, notification.getLink()) - .putExtra(EXTRA_OBJECT_ID, notification.getObjectId()) - .putExtra(EXTRA_SUBJECT, notification.getSubject()) - .putExtra(EXTRA_SUBJECT_RICH, notification.getSubjectRich()) - .putExtra(EXTRA_MESSAGE, notification.getMessage()) - .putExtra(EXTRA_MESSAGE_RICH, notification.getMessageRich()) - .putExtra(EXTRA_USER, notification.getUser()) - .putExtra(EXTRA_NID, notification.getNotificationId()) - } + private fun putExtrasToIntent(intent: Intent, notification: Notification, user: User): Intent = intent + .putExtra(EXTRA_ACCOUNT, user.accountName) + .putExtra(EXTRA_LINK, notification.getLink()) + .putExtra(EXTRA_OBJECT_ID, notification.getObjectId()) + .putExtra(EXTRA_SUBJECT, notification.getSubject()) + .putExtra(EXTRA_SUBJECT_RICH, notification.getSubjectRich()) + .putExtra(EXTRA_MESSAGE, notification.getMessage()) + .putExtra(EXTRA_MESSAGE_RICH, notification.getMessageRich()) + .putExtra(EXTRA_USER, notification.getUser()) + .putExtra(EXTRA_NID, notification.getNotificationId()) companion object { const val APP_NAME = "deck" diff --git a/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt b/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt index 6210523..ebcb5f7 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt @@ -8,7 +8,7 @@ * Copyright (C) 2017 Nextcloud GmbH. * Copyright (C) 2020 Chris Narkiewicz * -* SPDX-License-Identifier: AGPL-3.0-or-later +* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt index b008710..f0d58d9 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -23,6 +23,8 @@ import com.nextcloud.client.documentscan.GeneratePDFUseCase import com.nextcloud.client.documentscan.GeneratePdfFromImagesWork import com.nextcloud.client.integrations.deck.DeckApi import com.nextcloud.client.jobs.download.FileDownloadWorker +import com.nextcloud.client.jobs.metadata.MetadataWorker +import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.logger.Logger import com.nextcloud.client.network.ConnectivityService @@ -95,39 +97,42 @@ class BackgroundJobFactory @Inject constructor( GeneratePdfFromImagesWork::class -> createPDFGenerateWork(context, workerParameters) HealthStatusWork::class -> createHealthStatusWork(context, workerParameters) TestJob::class -> createTestJob(context, workerParameters) + OfflineOperationsWorker::class -> createOfflineOperationsWorker(context, workerParameters) + InternalTwoWaySyncWork::class -> createInternalTwoWaySyncWork(context, workerParameters) + MetadataWorker::class -> createMetadataWorker(context, workerParameters) else -> null // caller falls back to default factory } } } - private fun createFilesExportWork( - context: Context, - params: WorkerParameters - ): ListenableWorker { - return FilesExportWork( - context, + private fun createOfflineOperationsWorker(context: Context, params: WorkerParameters): ListenableWorker = + OfflineOperationsWorker( accountManager.user, - contentResolver, + context, + connectivityService, viewThemeUtils.get(), params ) - } - private fun createContentObserverJob( - context: Context, - workerParameters: WorkerParameters - ): ListenableWorker { - return ContentObserverWork( + private fun createFilesExportWork(context: Context, params: WorkerParameters): ListenableWorker = FilesExportWork( + context, + accountManager.user, + contentResolver, + viewThemeUtils.get(), + params + ) + + private fun createContentObserverJob(context: Context, workerParameters: WorkerParameters): ListenableWorker = + ContentObserverWork( context, workerParameters, SyncedFolderProvider(contentResolver, preferences, clock), powerManagementService, backgroundJobManager.get() ) - } - private fun createContactsBackupWork(context: Context, params: WorkerParameters): ContactsBackupWork { - return ContactsBackupWork( + private fun createContactsBackupWork(context: Context, params: WorkerParameters): ContactsBackupWork = + ContactsBackupWork( context, params, resources, @@ -135,63 +140,55 @@ class BackgroundJobFactory @Inject constructor( contentResolver, accountManager ) - } - private fun createContactsImportWork(context: Context, params: WorkerParameters): ContactsImportWork { - return ContactsImportWork( + private fun createContactsImportWork(context: Context, params: WorkerParameters): ContactsImportWork = + ContactsImportWork( context, params, logger, contentResolver ) - } - private fun createCalendarBackupWork(context: Context, params: WorkerParameters): CalendarBackupWork { - return CalendarBackupWork( + private fun createCalendarBackupWork(context: Context, params: WorkerParameters): CalendarBackupWork = + CalendarBackupWork( context, params, contentResolver, accountManager, preferences ) - } - private fun createCalendarImportWork(context: Context, params: WorkerParameters): CalendarImportWork { - return CalendarImportWork( + private fun createCalendarImportWork(context: Context, params: WorkerParameters): CalendarImportWork = + CalendarImportWork( context, params, logger, contentResolver ) - } - private fun createFilesSyncWork(context: Context, params: WorkerParameters): FilesSyncWork { - return FilesSyncWork( - context = context, - params = params, - contentResolver = contentResolver, - userAccountManager = accountManager, - uploadsStorageManager = uploadsStorageManager, - connectivityService = connectivityService, - powerManagementService = powerManagementService, - syncedFolderProvider = syncedFolderProvider, - backgroundJobManager = backgroundJobManager.get() - ) - } + private fun createFilesSyncWork(context: Context, params: WorkerParameters): FilesSyncWork = FilesSyncWork( + context = context, + params = params, + contentResolver = contentResolver, + userAccountManager = accountManager, + uploadsStorageManager = uploadsStorageManager, + connectivityService = connectivityService, + powerManagementService = powerManagementService, + syncedFolderProvider = syncedFolderProvider, + backgroundJobManager = backgroundJobManager.get() + ) - private fun createOfflineSyncWork(context: Context, params: WorkerParameters): OfflineSyncWork { - return OfflineSyncWork( - context = context, - params = params, - contentResolver = contentResolver, - userAccountManager = accountManager, - connectivityService = connectivityService, - powerManagementService = powerManagementService - ) - } + private fun createOfflineSyncWork(context: Context, params: WorkerParameters): OfflineSyncWork = OfflineSyncWork( + context = context, + params = params, + contentResolver = contentResolver, + userAccountManager = accountManager, + connectivityService = connectivityService, + powerManagementService = powerManagementService + ) - private fun createMediaFoldersDetectionWork(context: Context, params: WorkerParameters): MediaFoldersDetectionWork { - return MediaFoldersDetectionWork( + private fun createMediaFoldersDetectionWork(context: Context, params: WorkerParameters): MediaFoldersDetectionWork = + MediaFoldersDetectionWork( context, params, resources, @@ -202,21 +199,18 @@ class BackgroundJobFactory @Inject constructor( viewThemeUtils.get(), syncedFolderProvider ) - } - private fun createNotificationWork(context: Context, params: WorkerParameters): NotificationWork { - return NotificationWork( - context, - params, - notificationManager, - accountManager, - deckApi, - viewThemeUtils.get() - ) - } + private fun createNotificationWork(context: Context, params: WorkerParameters): NotificationWork = NotificationWork( + context, + params, + notificationManager, + accountManager, + deckApi, + viewThemeUtils.get() + ) - private fun createAccountRemovalWork(context: Context, params: WorkerParameters): AccountRemovalWork { - return AccountRemovalWork( + private fun createAccountRemovalWork(context: Context, params: WorkerParameters): AccountRemovalWork = + AccountRemovalWork( context, params, uploadsStorageManager, @@ -227,10 +221,9 @@ class BackgroundJobFactory @Inject constructor( preferences, syncedFolderProvider ) - } - private fun createFilesUploadWorker(context: Context, params: WorkerParameters): FileUploadWorker { - return FileUploadWorker( + private fun createFilesUploadWorker(context: Context, params: WorkerParameters): FileUploadWorker = + FileUploadWorker( uploadsStorageManager, connectivityService, powerManagementService, @@ -242,20 +235,18 @@ class BackgroundJobFactory @Inject constructor( context, params ) - } - private fun createFilesDownloadWorker(context: Context, params: WorkerParameters): FileDownloadWorker { - return FileDownloadWorker( + private fun createFilesDownloadWorker(context: Context, params: WorkerParameters): FileDownloadWorker = + FileDownloadWorker( viewThemeUtils.get(), accountManager, localBroadcastManager.get(), context, params ) - } - private fun createPDFGenerateWork(context: Context, params: WorkerParameters): GeneratePdfFromImagesWork { - return GeneratePdfFromImagesWork( + private fun createPDFGenerateWork(context: Context, params: WorkerParameters): GeneratePdfFromImagesWork = + GeneratePdfFromImagesWork( appContext = context, generatePdfUseCase = generatePdfUseCase, viewThemeUtils = viewThemeUtils.get(), @@ -264,23 +255,34 @@ class BackgroundJobFactory @Inject constructor( logger = logger, params = params ) - } - private fun createHealthStatusWork(context: Context, params: WorkerParameters): HealthStatusWork { - return HealthStatusWork( + private fun createHealthStatusWork(context: Context, params: WorkerParameters): HealthStatusWork = HealthStatusWork( + context, + params, + accountManager, + arbitraryDataProvider, + backgroundJobManager.get() + ) + + private fun createTestJob(context: Context, params: WorkerParameters): TestJob = TestJob( + context, + params, + backgroundJobManager.get() + ) + + private fun createInternalTwoWaySyncWork(context: Context, params: WorkerParameters): InternalTwoWaySyncWork = + InternalTwoWaySyncWork( context, params, accountManager, - arbitraryDataProvider, - backgroundJobManager.get() + powerManagementService, + connectivityService, + preferences ) - } - private fun createTestJob(context: Context, params: WorkerParameters): TestJob { - return TestJob( - context, - params, - backgroundJobManager.get() - ) - } + private fun createMetadataWorker(context: Context, params: WorkerParameters): MetadataWorker = MetadataWorker( + context, + params, + accountManager.user + ) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt index 68a7a02..2f9d311 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -95,7 +95,7 @@ interface BackgroundJobManager { * @param contactsAccountName Target contacts account name; null for local contacts * @param contactsAccountType Target contacts account type; null for local contacts * @param vCardFilePath Path to file containing all contact entries - * @param selectedContacts List of contact indices to import from [vCardFilePath] file + * @param selectedContactsFilePath File path of list of contact indices to import from [vCardFilePath] file * * @return Job info with current status; status is null if job does not exist */ @@ -103,7 +103,7 @@ interface BackgroundJobManager { contactsAccountName: String?, contactsAccountType: String?, vCardFilePath: String, - selectedContacts: IntArray + selectedContactsFilePath: String ): LiveData /** @@ -119,13 +119,19 @@ interface BackgroundJobManager { fun startImmediateFilesExportJob(files: Collection): LiveData - fun schedulePeriodicFilesSyncJob() + fun schedulePeriodicFilesSyncJob(syncedFolderID: Long) + /** + * Immediately start File Sync job for given syncFolderID. + */ fun startImmediateFilesSyncJob( + syncedFolderID: Long, overridePowerSaving: Boolean = false, - changedFiles: Array = arrayOf() + changedFiles: Array = arrayOf() ) + fun cancelTwoWaySyncJob() + fun scheduleOfflineSync() fun scheduleMediaFoldersDetectionJob() @@ -133,7 +139,7 @@ interface BackgroundJobManager { fun startNotificationJob(subject: String, signature: String) fun startAccountRemovalJob(accountName: String, remoteWipe: Boolean) - fun startFilesUploadJob(user: User) + fun startFilesUploadJob(user: User, uploadIds: LongArray, showSameFileAlreadyExistsNotification: Boolean) fun getFileUploads(user: User): LiveData> fun cancelFilesUploadJob(user: User) fun isStartFileUploadJobScheduled(user: User): Boolean @@ -163,4 +169,10 @@ interface BackgroundJobManager { fun cancelAllJobs() fun schedulePeriodicHealthStatus() fun startHealthStatus() + fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean + fun startOfflineOperations() + fun startPeriodicallyOfflineOperation() + fun scheduleInternal2WaySync(intervalMinutes: Long) + fun cancelAllFilesDownloadJobs() + fun startMetadataSyncJob(currentDirPath: String) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index d581604..5fd6b4f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -2,13 +2,14 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs import android.provider.MediaStore import androidx.lifecycle.LiveData import androidx.lifecycle.map +import androidx.work.BackoffPolicy import androidx.work.Constraints import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy @@ -26,11 +27,18 @@ import com.nextcloud.client.core.Clock import com.nextcloud.client.di.Injectable import com.nextcloud.client.documentscan.GeneratePdfFromImagesWork import com.nextcloud.client.jobs.download.FileDownloadWorker +import com.nextcloud.client.jobs.metadata.MetadataWorker +import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker +import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.utils.extensions.isWorkRunning import com.nextcloud.utils.extensions.isWorkScheduled import com.owncloud.android.datamodel.OCFile import com.owncloud.android.operations.DownloadType +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.util.Date import java.util.UUID import java.util.concurrent.TimeUnit @@ -55,10 +63,10 @@ internal class BackgroundJobManagerImpl( private val workManager: WorkManager, private val clock: Clock, private val preferences: AppPreferences -) : BackgroundJobManager, Injectable { +) : BackgroundJobManager, + Injectable { companion object { - const val TAG_ALL = "*" // This tag allows us to retrieve list of all jobs run by Nextcloud client const val JOB_CONTENT_OBSERVER = "content_observer" const val JOB_PERIODIC_CONTACTS_BACKUP = "periodic_contacts_backup" @@ -79,9 +87,12 @@ internal class BackgroundJobManagerImpl( const val JOB_PDF_GENERATION = "pdf_generation" const val JOB_IMMEDIATE_CALENDAR_BACKUP = "immediate_calendar_backup" const val JOB_IMMEDIATE_FILES_EXPORT = "immediate_files_export" - + const val JOB_OFFLINE_OPERATIONS = "offline_operations" + const val JOB_PERIODIC_OFFLINE_OPERATIONS = "periodic_offline_operations" const val JOB_PERIODIC_HEALTH_STATUS = "periodic_health_status" const val JOB_IMMEDIATE_HEALTH_STATUS = "immediate_health_status" + const val JOB_METADATA_SYNC = "metadata_sync" + const val JOB_INTERNAL_TWO_WAY_SYNC = "internal_two_way_sync" const val JOB_TEST = "test_job" @@ -95,16 +106,16 @@ internal class BackgroundJobManagerImpl( const val NOT_SET_VALUE = "not set" const val PERIODIC_BACKUP_INTERVAL_MINUTES = 24 * 60L const val DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES = 15L + const val OFFLINE_OPERATIONS_PERIODIC_JOB_INTERVAL_MINUTES = 5L const val DEFAULT_IMMEDIATE_JOB_DELAY_SEC = 3L + const val DEFAULT_BACKOFF_CRITERIA_DELAY_SEC = 300L private const val KEEP_LOG_MILLIS = 1000 * 60 * 60 * 24 * 3L - fun formatNameTag(name: String, user: User? = null): String { - return if (user == null) { - "$TAG_PREFIX_NAME:$name" - } else { - "$TAG_PREFIX_NAME:$name ${user.accountName}" - } + fun formatNameTag(name: String, user: User? = null): String = if (user == null) { + "$TAG_PREFIX_NAME:$name" + } else { + "$TAG_PREFIX_NAME:$name ${user.accountName}" } fun formatUserTag(user: User): String = "$TAG_PREFIX_USER:${user.accountName}" @@ -121,36 +132,32 @@ internal class BackgroundJobManagerImpl( } } - fun parseTimestamp(timestamp: String): Date { - return try { - val ms = timestamp.toLong() - Date(ms) - } catch (ex: NumberFormatException) { - Date(0) - } + fun parseTimestamp(timestamp: String): Date = try { + val ms = timestamp.toLong() + Date(ms) + } catch (ex: NumberFormatException) { + Date(0) } /** * Convert platform [androidx.work.WorkInfo] object into application-specific [JobInfo] model. * Conversion extracts work metadata from tags. */ - fun fromWorkInfo(info: WorkInfo?): JobInfo? { - return if (info != null) { - val metadata = mutableMapOf() - info.tags.forEach { parseTag(it)?.let { metadata[it.first] = it.second } } - val timestamp = parseTimestamp(metadata.get(TAG_PREFIX_START_TIMESTAMP) ?: "0") - JobInfo( - id = info.id, - state = info.state.toString(), - name = metadata.get(TAG_PREFIX_NAME) ?: NOT_SET_VALUE, - user = metadata.get(TAG_PREFIX_USER) ?: NOT_SET_VALUE, - started = timestamp, - progress = info.progress.getInt("progress", -1), - workerClass = metadata.get(TAG_PREFIX_CLASS) ?: NOT_SET_VALUE - ) - } else { - null - } + fun fromWorkInfo(info: WorkInfo?): JobInfo? = if (info != null) { + val metadata = mutableMapOf() + info.tags.forEach { parseTag(it)?.let { metadata[it.first] = it.second } } + val timestamp = parseTimestamp(metadata.get(TAG_PREFIX_START_TIMESTAMP) ?: "0") + JobInfo( + id = info.id, + state = info.state.toString(), + name = metadata.get(TAG_PREFIX_NAME) ?: NOT_SET_VALUE, + user = metadata.get(TAG_PREFIX_USER) ?: NOT_SET_VALUE, + started = timestamp, + progress = info.progress.getInt("progress", -1), + workerClass = metadata.get(TAG_PREFIX_CLASS) ?: NOT_SET_VALUE + ) + } else { + null } fun deleteOldLogs(logEntries: MutableList): MutableList { @@ -168,6 +175,8 @@ internal class BackgroundJobManagerImpl( } } + private val defaultDispatcherScope = CoroutineScope(Dispatchers.Default) + override fun logStartOfWorker(workerName: String?) { val logs = deleteOldLogs(preferences.readLogEntry().toMutableList()) @@ -195,13 +204,15 @@ internal class BackgroundJobManagerImpl( private fun oneTimeRequestBuilder( jobClass: KClass, jobName: String, - user: User? = null + user: User? = null, + constraints: Constraints = Constraints.Builder().build() ): OneTimeWorkRequest.Builder { val builder = OneTimeWorkRequest.Builder(jobClass.java) .addTag(TAG_ALL) .addTag(formatNameTag(jobName, user)) .addTag(formatTimeTag(clock.currentTime)) .addTag(formatClassTag(jobClass)) + .setConstraints(constraints) user?.let { builder.addTag(formatUserTag(it)) } return builder } @@ -214,7 +225,8 @@ internal class BackgroundJobManagerImpl( jobName: String, intervalMins: Long = DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES, flexIntervalMins: Long = DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES, - user: User? = null + user: User? = null, + constraints: Constraints = Constraints.Builder().build() ): PeriodicWorkRequest.Builder { val builder = PeriodicWorkRequest.Builder( jobClass.java, @@ -227,6 +239,7 @@ internal class BackgroundJobManagerImpl( .addTag(formatNameTag(jobName, user)) .addTag(formatTimeTag(clock.currentTime)) .addTag(formatClassTag(jobClass)) + .setConstraints(constraints) user?.let { builder.addTag(formatUserTag(it)) } return builder } @@ -298,13 +311,13 @@ internal class BackgroundJobManagerImpl( contactsAccountName: String?, contactsAccountType: String?, vCardFilePath: String, - selectedContacts: IntArray + selectedContactsFilePath: String ): LiveData { val data = Data.Builder() .putString(ContactsImportWork.ACCOUNT_NAME, contactsAccountName) .putString(ContactsImportWork.ACCOUNT_TYPE, contactsAccountType) .putString(ContactsImportWork.VCARD_FILE_PATH, vCardFilePath) - .putIntArray(ContactsImportWork.SELECTED_CONTACTS_INDICES, selectedContacts) + .putString(ContactsImportWork.SELECTED_CONTACTS_FILE_PATH, selectedContactsFilePath) .build() val constraints = Constraints.Builder() @@ -403,32 +416,134 @@ internal class BackgroundJobManagerImpl( workManager.cancelJob(JOB_PERIODIC_CALENDAR_BACKUP, user) } - override fun schedulePeriodicFilesSyncJob() { + override fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean = + workManager.isWorkRunning(JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID) && + workManager.isWorkRunning(JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID) + + override fun startPeriodicallyOfflineOperation() { + val inputData = Data.Builder() + .putString(OfflineOperationsWorker.JOB_NAME, JOB_PERIODIC_OFFLINE_OPERATIONS) + .build() + + val request = periodicRequestBuilder( + jobClass = OfflineOperationsWorker::class, + jobName = JOB_PERIODIC_OFFLINE_OPERATIONS, + intervalMins = OFFLINE_OPERATIONS_PERIODIC_JOB_INTERVAL_MINUTES + ) + .setInputData(inputData) + .build() + + workManager.enqueueUniquePeriodicWork( + JOB_PERIODIC_OFFLINE_OPERATIONS, + ExistingPeriodicWorkPolicy.UPDATE, + request + ) + } + + override fun startOfflineOperations() { + val inputData = Data.Builder() + .putString(OfflineOperationsWorker.JOB_NAME, JOB_OFFLINE_OPERATIONS) + .build() + + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + + // Backoff criteria define how the system should retry the task if it fails. + // LINEAR means each retry will be delayed linearly (e.g., 10s, 20s, 30s...) + // DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES is used as the initial delay duration. + val backoffCriteriaPolicy = BackoffPolicy.LINEAR + val backoffCriteriaDelay = DEFAULT_BACKOFF_CRITERIA_DELAY_SEC + + val request = + oneTimeRequestBuilder(OfflineOperationsWorker::class, JOB_OFFLINE_OPERATIONS, constraints = constraints) + .setBackoffCriteria( + backoffCriteriaPolicy, + backoffCriteriaDelay, + TimeUnit.SECONDS + ) + .setInputData(inputData) + .build() + + workManager.enqueueUniqueWork( + JOB_OFFLINE_OPERATIONS, + ExistingWorkPolicy.KEEP, + request + ) + } + + override fun schedulePeriodicFilesSyncJob(syncedFolderID: Long) { + val arguments = Data.Builder() + .putLong(FilesSyncWork.SYNCED_FOLDER_ID, syncedFolderID) + .build() + val request = periodicRequestBuilder( jobClass = FilesSyncWork::class, - jobName = JOB_PERIODIC_FILES_SYNC, + jobName = JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID, intervalMins = DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES - ).build() - workManager.enqueueUniquePeriodicWork(JOB_PERIODIC_FILES_SYNC, ExistingPeriodicWorkPolicy.REPLACE, request) + ) + .setInputData(arguments) + .build() + workManager.enqueueUniquePeriodicWork( + JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID, + ExistingPeriodicWorkPolicy.REPLACE, + request + ) } override fun startImmediateFilesSyncJob( + syncedFolderID: Long, overridePowerSaving: Boolean, - changedFiles: Array + changedFiles: Array ) { val arguments = Data.Builder() .putBoolean(FilesSyncWork.OVERRIDE_POWER_SAVING, overridePowerSaving) .putStringArray(FilesSyncWork.CHANGED_FILES, changedFiles) + .putLong(FilesSyncWork.SYNCED_FOLDER_ID, syncedFolderID) .build() val request = oneTimeRequestBuilder( jobClass = FilesSyncWork::class, - jobName = JOB_IMMEDIATE_FILES_SYNC + jobName = JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID ) .setInputData(arguments) .build() - workManager.enqueueUniqueWork(JOB_IMMEDIATE_FILES_SYNC, ExistingWorkPolicy.APPEND, request) + workManager.enqueueUniqueWork( + JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID, + ExistingWorkPolicy.APPEND, + request + ) + } + + override fun cancelTwoWaySyncJob() { + workManager.cancelJob(JOB_INTERNAL_TWO_WAY_SYNC) + } + + override fun cancelAllFilesDownloadJobs() { + workManager.cancelAllWorkByTag(formatClassTag(FileDownloadWorker::class)) + } + + override fun startMetadataSyncJob(currentDirPath: String) { + val inputData = Data.Builder() + .putString(MetadataWorker.FILE_PATH, currentDirPath) + .build() + + val constrains = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresBatteryNotLow(true) + .build() + + val request = oneTimeRequestBuilder(MetadataWorker::class, JOB_METADATA_SYNC) + .setConstraints(constrains) + .setInputData(inputData) + .build() + + workManager.enqueueUniqueWork( + JOB_METADATA_SYNC, + ExistingWorkPolicy.REPLACE, + request + ) } override fun scheduleOfflineSync() { @@ -491,34 +606,75 @@ internal class BackgroundJobManagerImpl( workManager.enqueue(request) } - private fun startFileUploadJobTag(user: User): String { - return JOB_FILES_UPLOAD + user.accountName + private fun startFileUploadJobTag(user: User): String = JOB_FILES_UPLOAD + user.accountName + + override fun isStartFileUploadJobScheduled(user: User): Boolean = + workManager.isWorkScheduled(startFileUploadJobTag(user)) + + /** + * This method supports initiating uploads for various scenarios, including: + * - New upload batches + * - Failed uploads + * - FilesSyncWork + * - ... + * + * @param user The user for whom the upload job is being created. + * @param uploadIds Array of upload IDs to be processed. These IDs originate from multiple sources + * and cannot be determined directly from the account name or a single function + * within the worker. + */ + override fun startFilesUploadJob(user: User, uploadIds: LongArray, showSameFileAlreadyExistsNotification: Boolean) { + defaultDispatcherScope.launch { + val batchSize = FileUploadHelper.MAX_FILE_COUNT + val batches = uploadIds.toList().chunked(batchSize) + val tag = startFileUploadJobTag(user) + + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + + val dataBuilder = Data.Builder() + .putBoolean( + FileUploadWorker.SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION, + showSameFileAlreadyExistsNotification + ) + .putString(FileUploadWorker.ACCOUNT, user.accountName) + .putInt(FileUploadWorker.TOTAL_UPLOAD_SIZE, uploadIds.size) + + val workRequests = batches.mapIndexed { index, batch -> + dataBuilder + .putLongArray(FileUploadWorker.UPLOAD_IDS, batch.toLongArray()) + .putInt(FileUploadWorker.CURRENT_BATCH_INDEX, index) + + oneTimeRequestBuilder(FileUploadWorker::class, JOB_FILES_UPLOAD, user) + .addTag(tag) + .setInputData(dataBuilder.build()) + .setConstraints(constraints) + .build() + } + + // Chain the work requests sequentially + if (workRequests.isNotEmpty()) { + var workChain = workManager.beginUniqueWork( + tag, + ExistingWorkPolicy.APPEND_OR_REPLACE, + workRequests.first() + ) + + workRequests.drop(1).forEach { request -> + workChain = workChain.then(request) + } + + workChain.enqueue() + } + } } - override fun isStartFileUploadJobScheduled(user: User): Boolean { - return workManager.isWorkScheduled(startFileUploadJobTag(user)) - } + private fun startFileDownloadJobTag(user: User, fileId: Long): String = + JOB_FOLDER_DOWNLOAD + user.accountName + fileId - override fun startFilesUploadJob(user: User) { - val data = workDataOf(FileUploadWorker.ACCOUNT to user.accountName) - - val tag = startFileUploadJobTag(user) - - val request = oneTimeRequestBuilder(FileUploadWorker::class, JOB_FILES_UPLOAD, user) - .addTag(tag) - .setInputData(data) - .build() - - workManager.enqueueUniqueWork(tag, ExistingWorkPolicy.KEEP, request) - } - - private fun startFileDownloadJobTag(user: User, fileId: Long): String { - return JOB_FOLDER_DOWNLOAD + user.accountName + fileId - } - - override fun isStartFileDownloadJobScheduled(user: User, fileId: Long): Boolean { - return workManager.isWorkScheduled(startFileDownloadJobTag(user, fileId)) - } + override fun isStartFileDownloadJobScheduled(user: User, fileId: Long): Boolean = + workManager.isWorkScheduled(startFileDownloadJobTag(user, fileId)) override fun startFileDownloadJob( user: User, @@ -546,7 +702,9 @@ internal class BackgroundJobManagerImpl( .setInputData(data) .build() - workManager.enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, request) + // Since for each file new FileDownloadWorker going to be scheduled, + // better to use ExistingWorkPolicy.KEEP policy. + workManager.enqueueUniqueWork(tag, ExistingWorkPolicy.KEEP, request) } override fun getFileUploads(user: User): LiveData> { @@ -625,4 +783,16 @@ internal class BackgroundJobManagerImpl( request ) } + + override fun scheduleInternal2WaySync(intervalMinutes: Long) { + val request = periodicRequestBuilder( + jobClass = InternalTwoWaySyncWork::class, + jobName = JOB_INTERNAL_TWO_WAY_SYNC, + intervalMins = intervalMinutes + ) + .setInitialDelay(intervalMinutes, TimeUnit.MINUTES) + .build() + + workManager.enqueueUniquePeriodicWork(JOB_INTERNAL_TWO_WAY_SYNC, ExistingPeriodicWorkPolicy.UPDATE, request) + } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/CalendarBackupWork.kt b/app/src/main/java/com/nextcloud/client/jobs/CalendarBackupWork.kt index decf554..ffd2485 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/CalendarBackupWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/CalendarBackupWork.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs diff --git a/app/src/main/java/com/nextcloud/client/jobs/CalendarImportWork.kt b/app/src/main/java/com/nextcloud/client/jobs/CalendarImportWork.kt index 5d4f112..03a4d42 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/CalendarImportWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/CalendarImportWork.kt @@ -1,10 +1,11 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -13,6 +14,7 @@ import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters import com.nextcloud.client.logger.Logger +import com.owncloud.android.lib.common.utils.Log_OC import net.fortuna.ical4j.data.CalendarBuilder import third_parties.sufficientlysecure.AndroidCalendar import third_parties.sufficientlysecure.CalendarSource @@ -28,37 +30,56 @@ class CalendarImportWork( companion object { const val TAG = "CalendarImportWork" - const val SELECTED_CALENDARS = "selected_contacts_indices" } + @Suppress("TooGenericExceptionCaught") override fun doWork(): Result { - val calendarPaths = inputData.getStringArray(SELECTED_CALENDARS) ?: arrayOf() - val calendars = inputData.keyValueMap as Map + val calendars = inputData.keyValueMap as? Map<*, *> + if (calendars == null) { + logger.d(TAG, "CalendarImportWork cancelled due to null empty input data") + return Result.failure() + } val calendarBuilder = CalendarBuilder() - for ((path, selectedCalendar) in calendars) { - logger.d(TAG, "Import calendar from $path") + for ((path, selectedCalendarIndex) in calendars) { + try { + if (path !is String || selectedCalendarIndex !is Int) { + logger.d(TAG, "Skipping wrong input data types: $path - $selectedCalendarIndex") + continue + } - val file = File(path) - val calendarSource = CalendarSource( - file.toURI().toURL().toString(), - null, - null, - null, - appContext - ) + logger.d(TAG, "Import calendar from $path") - val calendars = AndroidCalendar.loadAll(contentResolver)[0] + val file = File(path) + val calendarSource = CalendarSource( + file.toURI().toURL().toString(), + null, + null, + null, + appContext + ) - ProcessVEvent( - appContext, - calendarBuilder.build(calendarSource.stream), - selectedCalendar, - true - ).run() + val calendarList = AndroidCalendar.loadAll(contentResolver) + if (selectedCalendarIndex >= calendarList.size) { + logger.d(TAG, "Skipping selectedCalendarIndex out of bound") + continue + } + + val selectedCalendar = calendarList[selectedCalendarIndex] + + ProcessVEvent( + appContext, + calendarBuilder.build(calendarSource.stream), + selectedCalendar, + true + ).run() + } catch (e: Exception) { + Log_OC.e(TAG, "skipping calendarIndex: $selectedCalendarIndex due to: $e") + } } + logger.d(TAG, "CalendarImportWork successfully completed") return Result.success() } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt index 0d7ef0a..4a01346 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContactsImportWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContactsImportWork.kt index 9803ccd..2b231e4 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContactsImportWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContactsImportWork.kt @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -16,12 +16,16 @@ import android.provider.ContactsContract import androidx.work.Worker import androidx.work.WorkerParameters import com.nextcloud.client.logger.Logger +import com.nextcloud.utils.extensions.toIntArray +import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.fragment.contactsbackup.BackupListFragment import com.owncloud.android.ui.fragment.contactsbackup.VCardComparator import ezvcard.Ezvcard import ezvcard.VCard +import org.apache.commons.io.FileUtils import third_parties.ezvcard_android.ContactOperations import java.io.BufferedInputStream +import java.io.File import java.io.FileInputStream import java.io.IOException import java.util.Collections @@ -39,15 +43,27 @@ class ContactsImportWork( const val ACCOUNT_TYPE = "account_type" const val ACCOUNT_NAME = "account_name" const val VCARD_FILE_PATH = "vcard_file_path" - const val SELECTED_CONTACTS_INDICES = "selected_contacts_indices" + const val SELECTED_CONTACTS_FILE_PATH = "selected_contacts_file_path" } - @Suppress("ComplexMethod", "NestedBlockDepth") // legacy code + @Suppress("ComplexMethod", "NestedBlockDepth", "LongMethod", "ReturnCount") // legacy code override fun doWork(): Result { val vCardFilePath = inputData.getString(VCARD_FILE_PATH) ?: "" val contactsAccountName = inputData.getString(ACCOUNT_NAME) val contactsAccountType = inputData.getString(ACCOUNT_TYPE) - val selectedContactsIndices = inputData.getIntArray(SELECTED_CONTACTS_INDICES) ?: IntArray(0) + val selectedContactsFilePath = inputData.getString(SELECTED_CONTACTS_FILE_PATH) + if (selectedContactsFilePath == null) { + Log_OC.d(TAG, "selectedContactsFilePath is null") + return Result.failure() + } + + val selectedContactsFile = File(selectedContactsFilePath) + if (!selectedContactsFile.exists()) { + Log_OC.d(TAG, "selectedContactsFile not exists") + return Result.failure() + } + + val selectedContactsIndices = readCheckedContractsFromFile(selectedContactsFile) val inputStream = BufferedInputStream(FileInputStream(vCardFilePath)) val vCards = ArrayList() @@ -79,16 +95,21 @@ class ContactsImportWork( cursor.moveToNext() } } + for (contactIndex in selectedContactsIndices) { - val vCard = vCards[contactIndex] - if (BackupListFragment.getDisplayName(vCard).isEmpty()) { - if (!ownContactMap.containsKey(vCard)) { - operations.insertContact(vCard) + try { + val vCard = vCards[contactIndex] + if (BackupListFragment.getDisplayName(vCard).isEmpty()) { + if (!ownContactMap.containsKey(vCard)) { + operations.insertContact(vCard) + } else { + operations.updateContact(vCard, ownContactMap[vCard]) + } } else { - operations.updateContact(vCard, ownContactMap[vCard]) + operations.insertContact(vCard) // Insert All the contacts without name } - } else { - operations.insertContact(vCard) // Insert All the contacts without name + } catch (t: Throwable) { + Log_OC.e(TAG, "skipping contactIndex: $contactIndex due to: $t") } } } catch (e: Exception) { @@ -103,9 +124,20 @@ class ContactsImportWork( logger.e(TAG, "Error closing vCard stream", e) } + Log_OC.d(TAG, "ContractsImportWork successfully completed") + selectedContactsFile.delete() return Result.success() } + @Suppress("TooGenericExceptionCaught") + fun readCheckedContractsFromFile(file: File): IntArray = try { + val fileData = FileUtils.readFileToByteArray(file) + fileData.toIntArray() + } catch (e: Exception) { + Log_OC.e(TAG, "Exception readCheckedContractsFromFile: $e") + intArrayOf() + } + private fun getContactFromCursor(cursor: Cursor): VCard? { val lookupKey = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.LOOKUP_KEY)) val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey) diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index 718e527..d0394eb 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -12,6 +12,7 @@ import androidx.work.WorkerParameters import com.nextcloud.client.device.PowerManagementService import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.FilesSyncHelper /** * This work is triggered when OS detects change in media folders. @@ -23,7 +24,7 @@ import com.owncloud.android.lib.common.utils.Log_OC class ContentObserverWork( appContext: Context, private val params: WorkerParameters, - private val syncerFolderProvider: SyncedFolderProvider, + private val syncedFolderProvider: SyncedFolderProvider, private val powerManagementService: PowerManagementService, private val backgroundJobManager: BackgroundJobManager ) : Worker(appContext, params) { @@ -31,10 +32,12 @@ class ContentObserverWork( override fun doWork(): Result { backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) - if (params.triggeredContentUris.size > 0) { + if (params.triggeredContentUris.isNotEmpty()) { Log_OC.d(TAG, "File-sync Content Observer detected files change") checkAndStartFileSyncJob() backgroundJobManager.startMediaFoldersDetectionJob() + } else { + Log_OC.d(TAG, "triggeredContentUris empty") } recheduleSelf() @@ -48,13 +51,19 @@ class ContentObserverWork( } private fun checkAndStartFileSyncJob() { - val syncFolders = syncerFolderProvider.countEnabledSyncedFolders() > 0 - if (!powerManagementService.isPowerSavingEnabled && syncFolders) { + if (!powerManagementService.isPowerSavingEnabled && syncedFolderProvider.countEnabledSyncedFolders() > 0) { val changedFiles = mutableListOf() for (uri in params.triggeredContentUris) { changedFiles.add(uri.toString()) } - backgroundJobManager.startImmediateFilesSyncJob(false, changedFiles.toTypedArray()) + FilesSyncHelper.startFilesSyncForAllFolders( + syncedFolderProvider, + backgroundJobManager, + false, + changedFiles.toTypedArray() + ) + } else { + Log_OC.w(TAG, "cant startFilesSyncForAllFolders") } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt index 058500f..812be5c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -57,6 +57,8 @@ class FilesExportWork( } private fun exportFiles(fileIDs: LongArray): Int { + val fileDownloadHelper = FileDownloadHelper.instance() + var successfulExports = 0 fileIDs .asSequence() @@ -76,7 +78,11 @@ class FilesExportWork( showErrorNotification(successfulExports) } } else { - downloadFile(ocFile) + fileDownloadHelper.downloadFile( + user, + ocFile, + downloadType = DownloadType.EXPORT + ) } successfulExports++ @@ -95,14 +101,6 @@ class FilesExportWork( ) } - private fun downloadFile(ocFile: OCFile) { - FileDownloadHelper.instance().downloadFile( - user, - ocFile, - downloadType = DownloadType.EXPORT - ) - } - private fun showErrorNotification(successfulExports: Int) { val message = if (successfulExports == 0) { appContext.resources.getQuantityString(R.plurals.export_failed, successfulExports, successfulExports) diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt index 58b5728..b3c6cb2 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -1,21 +1,19 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2024 Jonas Mayer * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Mario Danic * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs import android.content.ContentResolver import android.content.Context import android.content.res.Resources -import android.os.Build import android.text.TextUtils -import androidx.core.app.NotificationCompat import androidx.exifinterface.media.ExifInterface -import androidx.work.ForegroundInfo import androidx.work.Worker import androidx.work.WorkerParameters import com.nextcloud.client.account.UserAccountManager @@ -25,10 +23,8 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.SubFolderRule import com.owncloud.android.R -import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.datamodel.FilesystemDataProvider -import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.datamodel.SyncedFolderProvider @@ -36,7 +32,6 @@ import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.SettingsActivity -import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.FilesSyncHelper import com.owncloud.android.utils.MimeType @@ -64,106 +59,172 @@ class FilesSyncWork( const val TAG = "FilesSyncJob" const val OVERRIDE_POWER_SAVING = "overridePowerSaving" const val CHANGED_FILES = "changedFiles" - const val FOREGROUND_SERVICE_ID = 414 + const val SYNCED_FOLDER_ID = "syncedFolderId" } - @Suppress("MagicNumber") - private fun updateForegroundWorker(progressPercent: Int, useForegroundWorker: Boolean) { - if (!useForegroundWorker) { - return - } + private lateinit var syncedFolder: SyncedFolder - // update throughout worker execution to give use feedback how far worker is - val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_FILE_SYNC) - .setTicker(context.getString(R.string.autoupload_worker_foreground_info)) - .setContentText(context.getString(R.string.autoupload_worker_foreground_info)) - .setSmallIcon(R.drawable.notification_icon) - .setContentTitle(context.getString(R.string.autoupload_worker_foreground_info)) - .setOngoing(true) - .setProgress(100, progressPercent, false) - .build() - val foregroundInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - ForegroundInfo(FOREGROUND_SERVICE_ID, notification, ForegroundServiceType.DataSync.getId()) - } else { - ForegroundInfo(FOREGROUND_SERVICE_ID, notification) - } - - setForegroundAsync(foregroundInfo) - } - - @Suppress("MagicNumber") + @Suppress("MagicNumber", "ReturnCount") override fun doWork(): Result { - backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) - Log_OC.d(TAG, "File-sync worker started") + val syncFolderId = inputData.getLong(SYNCED_FOLDER_ID, -1) + val changedFiles = inputData.getStringArray(CHANGED_FILES) - val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) - // If we are in power save mode, better to postpone upload - if (powerManagementService.isPowerSavingEnabled && !overridePowerSaving) { - val result = Result.success() - backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) - return result - } + backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class) + "_" + syncFolderId) + Log_OC.d(TAG, "AutoUpload started folder ID: $syncFolderId") + + // Create all the providers we'll need val resources = context.resources val lightVersion = resources.getBoolean(R.bool.syncedFolder_light) - FilesSyncHelper.restartJobsIfNeeded( + val filesystemDataProvider = FilesystemDataProvider(contentResolver) + val currentLocale = resources.configuration.locale + val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale) + dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) + + if (!setSyncedFolder(syncFolderId)) { + Log_OC.w(TAG, "AutoUpload skipped since syncedFolder ($syncFolderId) is not enabled!") + return logEndOfWorker(syncFolderId) + } + + // Always first try to schedule uploads to make sure files are uploaded even if worker was killed to early + uploadFilesFromFolder( + context, + resources, + lightVersion, + filesystemDataProvider, + currentLocale, + dateFormat, + syncedFolder + ) + + if (canExitEarly(changedFiles, syncFolderId)) { + Log_OC.w(TAG, "AutoUpload skipped canExit conditions are met") + return logEndOfWorker(syncFolderId) + } + + val user = userAccountManager.getUser(syncedFolder.account) + if (user.isPresent) { + var uploadIds = uploadsStorageManager.getCurrentUploadIds(user.get().accountName) + backgroundJobManager.startFilesUploadJob(user.get(), uploadIds, false) + } + + // Get changed files from ContentObserverWork (only images and videos) or by scanning filesystem + Log_OC.d( + TAG, + "AutoUpload (${syncedFolder.remotePath}) changed files from observer: " + + changedFiles.contentToString() + ) + collectChangedFiles(changedFiles) + Log_OC.d(TAG, "AutoUpload (${syncedFolder.remotePath}) finished checking files.") + + uploadFilesFromFolder( + context, + resources, + lightVersion, + filesystemDataProvider, + currentLocale, + dateFormat, + syncedFolder + ) + + FilesSyncHelper.restartUploadsIfNeeded( uploadsStorageManager, userAccountManager, connectivityService, powerManagementService ) - // Get changed files from ContentObserverWork (only images and videos) or by scanning filesystem - val changedFiles = inputData.getStringArray(CHANGED_FILES) - Log_OC.d(TAG, "File-sync worker changed files from observer: " + changedFiles.contentToString()) - collectChangedFiles(changedFiles) - Log_OC.d(TAG, "File-sync worker finished checking files.") + return logEndOfWorker(syncFolderId) + } - // Create all the providers we'll need - val filesystemDataProvider = FilesystemDataProvider(contentResolver) - val currentLocale = resources.configuration.locale - val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale) - dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) - - // start upload of changed / new files - val syncedFolders = syncedFolderProvider.syncedFolders - for ((index, syncedFolder) in syncedFolders.withIndex()) { - updateForegroundWorker( - (50 + (index.toDouble() / syncedFolders.size.toDouble()) * 50).toInt(), - changedFiles.isNullOrEmpty() - ) - if (syncedFolder.isEnabled) { - syncFolder( - context, - resources, - lightVersion, - filesystemDataProvider, - currentLocale, - dateFormat, - syncedFolder - ) - } - } - Log_OC.d(TAG, "File-sync worker finished") + private fun logEndOfWorker(syncFolderId: Long): Result { + Log_OC.d(TAG, "AutoUpload worker (${syncedFolder.remotePath}) finished") val result = Result.success() - backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) + backgroundJobManager.logEndOfWorker( + BackgroundJobManagerImpl.formatClassTag(this::class) + + "_" + syncFolderId, + result + ) return result } + private fun setSyncedFolder(syncedFolderID: Long): Boolean { + val syncedFolderTmp = syncedFolderProvider.getSyncedFolderByID(syncedFolderID) + if (syncedFolderTmp == null || !syncedFolderTmp.isEnabled) { + return false + } + syncedFolder = syncedFolderTmp + return true + } + + @Suppress("ReturnCount") + private fun canExitEarly(changedFiles: Array?, syncedFolderID: Long): Boolean { + // If we are in power save mode better to postpone scan and upload + val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) + if ((powerManagementService.isPowerSavingEnabled && !overridePowerSaving)) { + Log_OC.w(TAG, "AutoUpload skipped powerSaving is enabled!") + return true + } + + if (syncedFolderID < 0) { + Log_OC.w(TAG, "AutoUpload skipped no valid syncedFolderID provided") + return true + } + + // or sync worker already running + if (backgroundJobManager.bothFilesSyncJobsRunning(syncedFolderID)) { + Log_OC.w(TAG, "AutoUpload skipped another worker instance is running for $syncedFolderID") + return true + } + + val calculatedScanInterval = + FilesSyncHelper.calculateScanInterval(syncedFolder, connectivityService, powerManagementService) + val totalScanInterval = (syncedFolder.lastScanTimestampMs + calculatedScanInterval) + val currentTime = System.currentTimeMillis() + val passedScanInterval = totalScanInterval <= currentTime + + Log_OC.d(TAG, "AutoUpload lastScanTimestampMs: " + syncedFolder.lastScanTimestampMs) + Log_OC.d(TAG, "AutoUpload calculatedScanInterval: $calculatedScanInterval") + Log_OC.d(TAG, "AutoUpload totalScanInterval: $totalScanInterval") + Log_OC.d(TAG, "AutoUpload currentTime: $currentTime") + Log_OC.d(TAG, "AutoUpload passedScanInterval: $passedScanInterval") + + if (!passedScanInterval && changedFiles.isNullOrEmpty() && !overridePowerSaving) { + Log_OC.w( + TAG, + "AutoUpload skipped since started before scan interval and nothing todo: " + syncedFolder.localPath + ) + return true + } + + if (syncedFolder.isChargingOnly && + !powerManagementService.battery.isCharging && + !powerManagementService.battery.isFull + ) { + Log_OC.w( + TAG, + "AutoUpload skipped since phone is not charging: " + syncedFolder.localPath + ) + return true + } + + return false + } + @Suppress("MagicNumber") private fun collectChangedFiles(changedFiles: Array?) { if (!changedFiles.isNullOrEmpty()) { - FilesSyncHelper.insertChangedEntries(syncedFolderProvider, changedFiles) + FilesSyncHelper.insertChangedEntries(syncedFolder, changedFiles) } else { - // Check every file in every synced folder for changes and update - // filesystemDataProvider database (potentially needs a long time so use foreground worker) - updateForegroundWorker(5, true) - FilesSyncHelper.insertAllDBEntries(syncedFolderProvider) - updateForegroundWorker(50, true) + // Check every file in synced folder for changes and update + // filesystemDataProvider database (potentially needs a long time) + FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder) } + syncedFolder.lastScanTimestampMs = System.currentTimeMillis() + syncedFolderProvider.updateSyncFolder(syncedFolder) } @Suppress("LongMethod") // legacy code - private fun syncFolder( + private fun uploadFilesFromFolder( context: Context, resources: Resources, lightVersion: Boolean, @@ -175,66 +236,90 @@ class FilesSyncWork( val uploadAction: Int? val needsCharging: Boolean val needsWifi: Boolean - var file: File val accountName = syncedFolder.account + val optionalUser = userAccountManager.getUser(accountName) if (!optionalUser.isPresent) { + Log_OC.w(TAG, "AutoUpload:uploadFilesFromFolder skipped user not present") return } + val user = optionalUser.get() - val arbitraryDataProvider: ArbitraryDataProvider? = if (lightVersion) { + val arbitraryDataProvider = if (lightVersion) { ArbitraryDataProviderImpl(context) } else { null } + + // Ensure only new files are processed for upload. + // Files that have been previously uploaded cannot be re-uploaded, + // even if they have been deleted or moved from the target folder, + // as they are already marked as uploaded in the database. val paths = filesystemDataProvider.getFilesForUpload( syncedFolder.localPath, syncedFolder.id.toString() ) - - if (paths.size == 0) { + if (paths.isEmpty()) { + Log_OC.w(TAG, "AutoUpload:uploadFilesFromFolder skipped paths is empty") return } val pathsAndMimes = paths.map { path -> - file = File(path) + val file = File(path) val localPath = file.absolutePath + val remotePath = getRemotePath(file, syncedFolder, sFormatter, lightVersion, resources, currentLocale) + val mimeType = MimeTypeUtil.getBestMimeTypeByFilename(localPath) + + Log_OC.d(TAG, "AutoUpload:pathsAndMimes file.path: ${file.path}") + Log_OC.d(TAG, "AutoUpload:pathsAndMimes localPath: $localPath") + Log_OC.d(TAG, "AutoUpload:pathsAndMimes remotePath: $remotePath") + Log_OC.d(TAG, "AutoUpload:pathsAndMimes mimeType: $mimeType") + Triple( localPath, - getRemotePath(file, syncedFolder, sFormatter, lightVersion, resources, currentLocale), - MimeTypeUtil.getBestMimeTypeByFilename(localPath) + remotePath, + mimeType ) } + val localPaths = pathsAndMimes.map { it.first }.toTypedArray() val remotePaths = pathsAndMimes.map { it.second }.toTypedArray() if (lightVersion) { + Log_OC.d(TAG, "AutoUpload:uploadFilesFromFolder light version is used") + needsCharging = resources.getBoolean(R.bool.syncedFolder_light_on_charging) - needsWifi = arbitraryDataProvider!!.getBooleanValue( + needsWifi = arbitraryDataProvider?.getBooleanValue( accountName, SettingsActivity.SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI - ) + ) ?: true + val uploadActionString = resources.getString(R.string.syncedFolder_light_upload_behaviour) uploadAction = getUploadAction(uploadActionString) + Log_OC.d(TAG, "AutoUpload upload action is: $uploadAction") } else { + Log_OC.d(TAG, "AutoUpload:uploadFilesFromFolder not light version is used") + needsCharging = syncedFolder.isChargingOnly needsWifi = syncedFolder.isWifiOnly uploadAction = syncedFolder.uploadAction } + FileUploadHelper.instance().uploadNewFiles( user, localPaths, remotePaths, - uploadAction!!, - true, // create parent folder if not existent + uploadAction, + // create parent folder if not existent + true, UploadFileOperation.CREATED_AS_INSTANT_PICTURE, needsWifi, needsCharging, - syncedFolder.nameCollisionPolicy + syncedFolder.nameCollisionPolicy, + false ) for (path in paths) { - // TODO batch update filesystemDataProvider.updateFilesystemFileAsSentForUpload( path, syncedFolder.id.toString() @@ -255,10 +340,14 @@ class FilesSyncWork( val useSubfolders: Boolean val subFolderRule: SubFolderRule if (lightVersion) { + Log_OC.d(TAG, "AutoUpload:getRemotePath light version is used") + useSubfolders = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders) remoteFolder = resources.getString(R.string.syncedFolder_remote_folder) subFolderRule = SubFolderRule.YEAR_MONTH } else { + Log_OC.d(TAG, "AutoUpload:getRemotePath not light version is used") + useSubfolders = syncedFolder.isSubfolderByDate remoteFolder = syncedFolder.remotePath subFolderRule = syncedFolder.subfolderRule @@ -286,6 +375,8 @@ class FilesSyncWork( ): Long { var lastModificationTime = file.lastModified() if (MediaFolderType.IMAGE == syncedFolder.type && hasExif(file)) { + Log_OC.d(TAG, "AutoUpload:calculateLastModificationTime exif found") + @Suppress("TooGenericExceptionCaught") // legacy code try { val exifInterface = ExifInterface(file.absolutePath) @@ -294,6 +385,9 @@ class FilesSyncWork( val pos = ParsePosition(0) val dateTime = formatter.parse(exifDate, pos) lastModificationTime = dateTime.time + Log_OC.w(TAG, "AutoUpload:calculateLastModificationTime calculatedTime is: $lastModificationTime") + } else { + Log_OC.w(TAG, "AutoUpload:calculateLastModificationTime exifDate is empty") } } catch (e: Exception) { Log_OC.d(TAG, "Failed to get the proper time " + e.localizedMessage) @@ -302,12 +396,10 @@ class FilesSyncWork( return lastModificationTime } - private fun getUploadAction(action: String): Int? { - return when (action) { - "LOCAL_BEHAVIOUR_FORGET" -> FileUploadWorker.LOCAL_BEHAVIOUR_FORGET - "LOCAL_BEHAVIOUR_MOVE" -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE - "LOCAL_BEHAVIOUR_DELETE" -> FileUploadWorker.LOCAL_BEHAVIOUR_DELETE - else -> FileUploadWorker.LOCAL_BEHAVIOUR_FORGET - } + private fun getUploadAction(action: String): Int = when (action) { + "LOCAL_BEHAVIOUR_FORGET" -> FileUploadWorker.LOCAL_BEHAVIOUR_FORGET + "LOCAL_BEHAVIOUR_MOVE" -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE + "LOCAL_BEHAVIOUR_DELETE" -> FileUploadWorker.LOCAL_BEHAVIOUR_DELETE + else -> FileUploadWorker.LOCAL_BEHAVIOUR_FORGET } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/HealthStatusWork.kt b/app/src/main/java/com/nextcloud/client/jobs/HealthStatusWork.kt index c658839..2e0fadb 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/HealthStatusWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/HealthStatusWork.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs diff --git a/app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt new file mode 100644 index 0000000..1d1c89f --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt @@ -0,0 +1,136 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Tobias Kaminsky + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.device.PowerManagementService +import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.client.preferences.AppPreferences +import com.owncloud.android.MainApp +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.SynchronizeFolderOperation +import com.owncloud.android.utils.FileStorageUtils +import java.io.File + +@Suppress("Detekt.NestedBlockDepth", "ReturnCount", "LongParameterList") +class InternalTwoWaySyncWork( + private val context: Context, + params: WorkerParameters, + private val userAccountManager: UserAccountManager, + private val powerManagementService: PowerManagementService, + private val connectivityService: ConnectivityService, + private val appPreferences: AppPreferences +) : Worker(context, params) { + private var shouldRun = true + private var operation: SynchronizeFolderOperation? = null + + override fun doWork(): Result { + Log_OC.d(TAG, "Worker started!") + + var result = true + + @Suppress("ComplexCondition") + if (!appPreferences.isTwoWaySyncEnabled || + powerManagementService.isPowerSavingEnabled || + !connectivityService.isConnected || + connectivityService.isInternetWalled || + !connectivityService.connectivity.isWifi + ) { + Log_OC.d(TAG, "Not starting due to constraints!") + return Result.success() + } + + val users = userAccountManager.allUsers + + for (user in users) { + val fileDataStorageManager = FileDataStorageManager(user, context.contentResolver) + val folders = fileDataStorageManager.getInternalTwoWaySyncFolders(user) + + for (folder in folders) { + if (!shouldRun) { + Log_OC.d(TAG, "Worker was stopped!") + return Result.failure() + } + + checkFreeSpace(folder)?.let { checkFreeSpaceResult -> + return checkFreeSpaceResult + } + + Log_OC.d(TAG, "Folder ${folder.remotePath}: started!") + operation = SynchronizeFolderOperation(context, folder.remotePath, user, fileDataStorageManager, true) + val operationResult = operation?.execute(context) + + if (operationResult?.isSuccess == true) { + Log_OC.d(TAG, "Folder ${folder.remotePath}: finished!") + } else { + Log_OC.d(TAG, "Folder ${folder.remotePath} failed!") + result = false + } + + folder.apply { + operationResult?.let { + internalFolderSyncResult = it.code.toString() + } + + internalFolderSyncTimestamp = System.currentTimeMillis() + } + + fileDataStorageManager.saveFile(folder) + } + } + + return if (result) { + Log_OC.d(TAG, "Worker finished with success!") + Result.success() + } else { + Log_OC.d(TAG, "Worker finished with failure!") + Result.failure() + } + } + + override fun onStopped() { + Log_OC.d(TAG, "OnStopped of worker called!") + operation?.cancel() + shouldRun = false + super.onStopped() + } + + @Suppress("TooGenericExceptionCaught") + private fun checkFreeSpace(folder: OCFile): Result? { + val storagePath = folder.storagePath ?: MainApp.getStoragePath() + val file = File(storagePath) + + if (!file.exists()) return null + + return try { + val freeSpaceLeft = file.freeSpace + val localFolder = File(storagePath, MainApp.getDataFolder()) + val localFolderSize = FileStorageUtils.getFolderSize(localFolder) + val remoteFolderSize = folder.fileLength + + if (freeSpaceLeft < (remoteFolderSize - localFolderSize)) { + Log_OC.d(TAG, "Not enough space left!") + Result.failure() + } else { + null + } + } catch (e: Exception) { + Log_OC.d(TAG, "Error caught at checkFreeSpace: $e") + null + } + } + + companion object { + const val TAG = "InternalTwoWaySyncWork" + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/JobInfo.kt b/app/src/main/java/com/nextcloud/client/jobs/JobInfo.kt index 935964c..b23b933 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/JobInfo.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/JobInfo.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs diff --git a/app/src/main/java/com/nextcloud/client/jobs/JobsModule.kt b/app/src/main/java/com/nextcloud/client/jobs/JobsModule.kt index f05e9a0..3f33145 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/JobsModule.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/JobsModule.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -27,9 +27,7 @@ class JobsModule { .build() val contextWrapper = object : ContextWrapper(context) { - override fun getApplicationContext(): Context { - return this - } + override fun getApplicationContext(): Context = this } WorkManager.initialize(contextWrapper, configuration) @@ -42,7 +40,5 @@ class JobsModule { workManager: WorkManager, clock: Clock, preferences: AppPreferences - ): BackgroundJobManager { - return BackgroundJobManagerImpl(workManager, clock, preferences) - } + ): BackgroundJobManager = BackgroundJobManagerImpl(workManager, clock, preferences) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt b/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt index eec7370..354b8a1 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt @@ -8,7 +8,7 @@ * Copyright (C) 2018 Andy Scherzinger * Copyright (C) 2020 Chris Narkiewicz * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -41,7 +41,7 @@ import com.owncloud.android.datamodel.MediaFoldersModel import com.owncloud.android.datamodel.MediaProvider import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.ui.activity.ManageAccountsActivity.PENDING_FOR_REMOVAL +import com.owncloud.android.ui.activity.ManageAccountsActivity import com.owncloud.android.ui.activity.SyncedFoldersActivity import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.SyncedFolderUtils @@ -73,7 +73,7 @@ class MediaFoldersDetectionWork constructor( private val randomIdGenerator = Random(clock.currentTime) - @Suppress("LongMethod", "ComplexMethod", "NestedBlockDepth") // legacy code + @Suppress("LongMethod", "ComplexMethod", "NestedBlockDepth", "ReturnCount") // legacy code override fun doWork(): Result { val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(context) val gson = Gson() @@ -134,7 +134,7 @@ class MediaFoldersDetectionWork constructor( val allUsers = userAccountManager.allUsers val activeUsers: MutableList = ArrayList() for (user in allUsers) { - if (!arbitraryDataProvider.getBooleanValue(user, PENDING_FOR_REMOVAL)) { + if (!arbitraryDataProvider.getBooleanValue(user, ManageAccountsActivity.PENDING_FOR_REMOVAL)) { activeUsers.add(user) } } @@ -190,6 +190,7 @@ class MediaFoldersDetectionWork constructor( gson.toJson(mediaFoldersModel) ) } + return Result.success() } diff --git a/app/src/main/java/com/nextcloud/client/jobs/NotificationWork.kt b/app/src/main/java/com/nextcloud/client/jobs/NotificationWork.kt index ea8b75c..f95e110 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/NotificationWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/NotificationWork.kt @@ -2,10 +2,11 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs +import android.Manifest import android.accounts.AuthenticatorException import android.accounts.OperationCanceledException import android.app.Activity @@ -14,10 +15,12 @@ import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.graphics.BitmapFactory import android.media.RingtoneManager import android.text.TextUtils import android.util.Base64 +import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.work.Worker @@ -29,6 +32,7 @@ import com.nextcloud.client.integrations.deck.DeckApi import com.owncloud.android.R import com.owncloud.android.datamodel.DecryptedPushMessage import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.OwnCloudClientFactory import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.utils.Log_OC @@ -223,8 +227,17 @@ class NotificationWork constructor( } .build() ) - val notificationManager = NotificationManagerCompat.from(context) - notificationManager.notify(notification.getNotificationId(), notificationBuilder.build()) + + if (ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + Log_OC.w(this, "Missing permission to post notifications") + } else { + val notificationManager = NotificationManagerCompat.from(context) + notificationManager.notify(notification.getNotificationId(), notificationBuilder.build()) + } } @Suppress("TooGenericExceptionCaught") // legacy code @@ -236,8 +249,7 @@ class NotificationWork constructor( } val user = optionalUser.get() try { - val client = OwnCloudClientManagerFactory.getDefaultSingleton() - .getClientFor(user.toOwnCloudAccount(), context) + val client = OwnCloudClientFactory.createNextcloudClient(user, context) val result = GetNotificationRemoteOperation(decryptedPushMessage.nid) .execute(client) if (result.isSuccess) { @@ -287,6 +299,7 @@ class NotificationWork constructor( val user = optionalUser.get() val client = OwnCloudClientManagerFactory.getDefaultSingleton() .getClientFor(user.toOwnCloudAccount(), context) + val nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, context) val actionType = intent.getStringExtra(KEY_NOTIFICATION_ACTION_TYPE) val actionLink = intent.getStringExtra(KEY_NOTIFICATION_ACTION_LINK) val success: Boolean = if (!actionType.isNullOrEmpty() && !actionLink.isNullOrEmpty()) { @@ -294,7 +307,7 @@ class NotificationWork constructor( resultCode == HttpStatus.SC_OK || resultCode == HttpStatus.SC_ACCEPTED } else { DeleteNotificationRemoteOperation(numericNotificationId) - .execute(client).isSuccess + .execute(nextcloudClient).isSuccess } if (success) { if (oldNotification == null) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt index d2ee5ce..26cddf0 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt @@ -6,7 +6,7 @@ * Copyright (C) 2018 Mario Danic * Copyright (C) 2020 Chris Narkiewicz * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -28,7 +28,7 @@ import com.owncloud.android.utils.FileStorageUtils import java.io.File @Suppress("LongParameterList") // Legacy code -class OfflineSyncWork constructor( +class OfflineSyncWork( private val context: Context, params: WorkerParameters, private val contentResolver: ContentResolver, @@ -65,7 +65,7 @@ class OfflineSyncWork constructor( return } - val updatedEtag = checkEtagChanged(folderName, storageManager, user) ?: return + val updatedEtag = checkETagChanged(folderName, storageManager, user) ?: return // iterate over downloaded files val files = folder.listFiles { obj: File -> obj.isFile } @@ -77,7 +77,9 @@ class OfflineSyncWork constructor( user, true, context, - storageManager + storageManager, + true, + false ) synchronizeFileOperation.execute(context) } @@ -101,41 +103,39 @@ class OfflineSyncWork constructor( } /** - * @return new etag if changed, `null` otherwise + * @return new eTag if changed, `null` otherwise */ - private fun checkEtagChanged(folderName: String, storageManager: FileDataStorageManager, user: User): String? { - val ocFolder = storageManager.getFileByPath(folderName) ?: return null + private fun checkETagChanged(folderName: String, storageManager: FileDataStorageManager, user: User): String? { + val folder = storageManager.getFileByEncryptedRemotePath(folderName) ?: return null - Log_OC.d(TAG, "$folderName: currentEtag: ${ocFolder.etag}") + Log_OC.d(TAG, "$folderName: current eTag: ${folder.etag}") // check for etag change, if false, skip - val checkEtagOperation = CheckEtagRemoteOperation( - ocFolder.remotePath, - ocFolder.etagOnServer - ) - val result = checkEtagOperation.execute(user, context) + val operation = CheckEtagRemoteOperation(folder.remotePath, folder.etagOnServer) + val result = operation.execute(user, context) + return when (result.code) { ResultCode.ETAG_UNCHANGED -> { Log_OC.d(TAG, "$folderName: eTag unchanged") null } ResultCode.FILE_NOT_FOUND -> { - val removalResult = storageManager.removeFolder(ocFolder, true, true) + val removalResult = storageManager.removeFolder(folder, true, true) if (!removalResult) { - Log_OC.e(TAG, "removal of " + ocFolder.storagePath + " failed: file not found") + Log_OC.e(TAG, "removal of " + folder.storagePath + " failed: file not found") } null } ResultCode.ETAG_CHANGED -> { Log_OC.d(TAG, "$folderName: eTag changed") - result.data[0] as String + result?.data?.get(0) as? String } else -> if (connectivityService.isInternetWalled) { Log_OC.d(TAG, "No connectivity, skipping sync") null } else { Log_OC.d(TAG, "$folderName: eTag changed") - result.data[0] as String + result?.data?.get(0) as? String } } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/TestJob.kt b/app/src/main/java/com/nextcloud/client/jobs/TestJob.kt index c30f2cb..2d70042 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/TestJob.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/TestJob.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs @@ -11,11 +11,8 @@ import androidx.work.Data import androidx.work.Worker import androidx.work.WorkerParameters -class TestJob( - appContext: Context, - params: WorkerParameters, - private val backgroundJobManager: BackgroundJobManager -) : Worker(appContext, params) { +class TestJob(appContext: Context, params: WorkerParameters, private val backgroundJobManager: BackgroundJobManager) : + Worker(appContext, params) { companion object { private const val MAX_PROGRESS = 100 diff --git a/app/src/main/java/com/nextcloud/client/jobs/clipboard/ClipboardClearWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/clipboard/ClipboardClearWorker.kt new file mode 100644 index 0000000..5adc17d --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/clipboard/ClipboardClearWorker.kt @@ -0,0 +1,49 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.clipboard + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Build +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.owncloud.android.lib.common.utils.Log_OC + +class ClipboardClearWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) { + private val tag = ClipboardClearWorker::class.java.name + + companion object { + const val CLIPBOARD_TEXT = "clipboard_text" + } + + @Suppress("TooGenericExceptionCaught", "ReturnCount") + override fun doWork(): Result { + try { + val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val currentClip = clipboardManager.primaryClip ?: return Result.success() + val clipboardText = currentClip.getItemAt(0).text?.toString() ?: return Result.success() + val copiedText = inputData.getString(CLIPBOARD_TEXT) + if (copiedText != clipboardText) { + return Result.success() + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + clipboardManager.clearPrimaryClip() + } else { + val newEmptyClip = ClipData.newPlainText("EmptyClipContent", "") + clipboardManager.setPrimaryClip(newEmptyClip) + } + + return Result.success() + } catch (e: Exception) { + Log_OC.e(tag, "Error in clipboard clear worker", e) + return Result.retry() + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt index a656ad8..9ccb36b 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt @@ -1,24 +1,18 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.download -import android.app.Notification -import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent -import android.graphics.BitmapFactory -import android.os.Build -import android.os.Handler -import android.os.Looper -import androidx.core.app.NotificationCompat +import com.nextcloud.client.jobs.notification.WorkerNotificationManager +import com.nextcloud.utils.numberFormatter.NumberFormatter import com.owncloud.android.R -import com.owncloud.android.lib.resources.files.FileUtils import com.owncloud.android.operations.DownloadFileOperation import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.theme.ViewThemeUtils @@ -26,52 +20,28 @@ import java.io.File import java.security.SecureRandom @Suppress("TooManyFunctions") -class DownloadNotificationManager( - private val id: Int, - private val context: Context, - private val viewThemeUtils: ViewThemeUtils -) { - private var notification: Notification - private var notificationBuilder: NotificationCompat.Builder - private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager +class DownloadNotificationManager(id: Int, private val context: Context, viewThemeUtils: ViewThemeUtils) : + WorkerNotificationManager( + id, + context, + viewThemeUtils, + tickerId = R.string.downloader_download_in_progress_ticker, + channelId = NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD + ) { - init { - notificationBuilder = NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { - setContentTitle(context.getString(R.string.downloader_download_in_progress_ticker)) - setTicker(context.getString(R.string.downloader_download_in_progress_ticker)) - setSmallIcon(R.drawable.notification_icon) - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon)) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) - } - } - - notification = notificationBuilder.build() - } + private var lastPercent = -1 @Suppress("MagicNumber") fun prepareForStart(operation: DownloadFileOperation) { - notificationBuilder = NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { - setSmallIcon(R.drawable.notification_icon) - setOngoing(true) + currentOperationTitle = File(operation.savePath).name + + notificationBuilder.run { + setContentTitle(currentOperationTitle) + setOngoing(false) setProgress(100, 0, operation.size < 0) - setContentText( - String.format( - context.getString(R.string.downloader_download_in_progress), 0, - File(operation.savePath).name - ) - ) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) - } - - notificationManager.notify( - id, - this.build() - ) } + + showNotification() } fun prepareForResult() { @@ -82,23 +52,21 @@ class DownloadNotificationManager( } @Suppress("MagicNumber") - fun updateDownloadProgress(filePath: String, percent: Int, totalToTransfer: Long) { - notificationBuilder.run { - setProgress(100, percent, totalToTransfer < 0) - val fileName: String = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1) - val text = - String.format(context.getString(R.string.downloader_download_in_progress), percent, fileName) - val title = - context.getString(R.string.downloader_download_in_progress_ticker) - updateNotificationText(title, text) + fun updateDownloadProgress(percent: Int, totalToTransfer: Long) { + // If downloads are so fast, no need to notify again. + if (percent == lastPercent) { + return } + lastPercent = percent + + val progressText = NumberFormatter.getPercentageText(percent) + setProgress(percent, progressText, totalToTransfer < 0) + showNotification() } @Suppress("MagicNumber") fun dismissNotification() { - Handler(Looper.getMainLooper()).postDelayed({ - notificationManager.cancel(id) - }, 2000) + dismissNotification(2000) } fun showNewNotification(text: String) { @@ -106,24 +74,12 @@ class DownloadNotificationManager( notificationBuilder.run { setProgress(0, 0, false) - setContentTitle(null) - setContentText(text) + setContentTitle(text) setOngoing(false) notificationManager.notify(notifyId, this.build()) } } - private fun updateNotificationText(title: String?, text: String) { - notificationBuilder.run { - title?.let { - setContentTitle(title) - } - - setContentText(text) - notificationManager.notify(id, this.build()) - } - } - fun setContentIntent(intent: Intent, flag: Int) { notificationBuilder.setContentIntent( PendingIntent.getActivity( @@ -134,12 +90,4 @@ class DownloadNotificationManager( ) ) } - - fun getId(): Int { - return id - } - - fun getNotification(): Notification { - return notificationBuilder.build() - } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/DownloadTask.kt b/app/src/main/java/com/nextcloud/client/jobs/download/DownloadTask.kt index 692f869..b8708df 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/DownloadTask.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/DownloadTask.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.download @@ -45,9 +45,7 @@ class DownloadTask( private val clientProvider: () -> OwnCloudClient, private val contentResolver: ContentResolver ) { - fun create(): DownloadTask { - return DownloadTask(context, contentResolver, clientProvider) - } + fun create(): DownloadTask = DownloadTask(context, contentResolver, clientProvider) } // Unused progress, isCancelled arguments needed for TransferManagerTest diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt index f6a2d06..cb027e9 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt @@ -1,12 +1,13 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.download enum class FileDownloadError { - Failed, Cancelled + Failed, + Cancelled } diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt index 664afee..6f1f96c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.download @@ -30,10 +30,8 @@ class FileDownloadHelper { companion object { private var instance: FileDownloadHelper? = null - fun instance(): FileDownloadHelper { - return instance ?: synchronized(this) { - instance ?: FileDownloadHelper().also { instance = it } - } + fun instance(): FileDownloadHelper = instance ?: synchronized(this) { + instance ?: FileDownloadHelper().also { instance = it } } } @@ -50,11 +48,13 @@ class FileDownloadHelper { val topParentId = fileStorageManager.getTopParentId(file) val isJobScheduled = backgroundJobManager.isStartFileDownloadJobScheduled(user, file.fileId) - return isJobScheduled || if (file.isFolder) { - backgroundJobManager.isStartFileDownloadJobScheduled(user, topParentId) - } else { - FileDownloadWorker.isDownloading(user.accountName, file.fileId) - } + return isJobScheduled || + if (file.isFolder) { + FileDownloadWorker.isDownloadingFolder(file.fileId) && + backgroundJobManager.isStartFileDownloadJobScheduled(user, topParentId) + } else { + FileDownloadWorker.isDownloading(user.accountName, file.fileId) + } } fun cancelPendingOrCurrentDownloads(user: User?, files: List?) { @@ -81,11 +81,7 @@ class FileDownloadHelper { backgroundJobManager.cancelFilesDownloadJob(currentUser, currentFile.fileId) } - fun saveFile( - file: OCFile, - currentDownload: DownloadFileOperation?, - storageManager: FileDataStorageManager? - ) { + fun saveFile(file: OCFile, currentDownload: DownloadFileOperation?, storageManager: FileDataStorageManager?) { val syncDate = System.currentTimeMillis() file.apply { diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt index 26f3a15..5077122 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.download @@ -22,63 +22,53 @@ import com.owncloud.android.ui.preview.PreviewImageFragment class FileDownloadIntents(private val context: Context) { - fun newDownloadIntent( - download: DownloadFileOperation, - linkedToRemotePath: String - ): Intent { - return Intent(FileDownloadWorker.getDownloadAddedMessage()).apply { + fun newDownloadIntent(download: DownloadFileOperation, linkedToRemotePath: String): Intent = + Intent(FileDownloadWorker.getDownloadAddedMessage()).apply { putExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME, download.user.accountName) putExtra(FileDownloadWorker.EXTRA_REMOTE_PATH, download.remotePath) putExtra(FileDownloadWorker.EXTRA_LINKED_TO_PATH, linkedToRemotePath) setPackage(context.packageName) } - } fun downloadFinishedIntent( download: DownloadFileOperation, downloadResult: RemoteOperationResult<*>, unlinkedFromRemotePath: String? - ): Intent { - return Intent(FileDownloadWorker.getDownloadFinishMessage()).apply { - putExtra(FileDownloadWorker.EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess) - putExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME, download.user.accountName) - putExtra(FileDownloadWorker.EXTRA_REMOTE_PATH, download.remotePath) - putExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR, download.behaviour) - putExtra(SendShareDialog.ACTIVITY_NAME, download.activityName) - putExtra(SendShareDialog.PACKAGE_NAME, download.packageName) - if (unlinkedFromRemotePath != null) { - putExtra(FileDownloadWorker.EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath) - } - setPackage(context.packageName) + ): Intent = Intent(FileDownloadWorker.getDownloadFinishMessage()).apply { + putExtra(FileDownloadWorker.EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess) + putExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME, download.user.accountName) + putExtra(FileDownloadWorker.EXTRA_REMOTE_PATH, download.remotePath) + putExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR, download.behaviour) + putExtra(SendShareDialog.ACTIVITY_NAME, download.activityName) + putExtra(SendShareDialog.PACKAGE_NAME, download.packageName) + if (unlinkedFromRemotePath != null) { + putExtra(FileDownloadWorker.EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath) } + setPackage(context.packageName) } - fun credentialContentIntent(user: User): Intent { - return Intent(context, AuthenticatorActivity::class.java).apply { - putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, user.toPlatformAccount()) - putExtra( - AuthenticatorActivity.EXTRA_ACTION, - AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN - ) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - addFlags(Intent.FLAG_FROM_BACKGROUND) - } + fun credentialContentIntent(user: User): Intent = Intent(context, AuthenticatorActivity::class.java).apply { + putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, user.toPlatformAccount()) + putExtra( + AuthenticatorActivity.EXTRA_ACTION, + AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN + ) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + addFlags(Intent.FLAG_FROM_BACKGROUND) } - fun detailsIntent(operation: DownloadFileOperation?): Intent { - return if (operation != null) { - if (PreviewImageFragment.canBePreviewed(operation.file)) { - Intent(context, PreviewImageActivity::class.java) - } else { - Intent(context, FileDisplayActivity::class.java) - }.apply { - putExtra(FileActivity.EXTRA_FILE, operation.file) - putExtra(FileActivity.EXTRA_USER, operation.user) - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - } + fun detailsIntent(operation: DownloadFileOperation?): Intent = if (operation != null) { + if (PreviewImageFragment.canBePreviewed(operation.file)) { + Intent(context, PreviewImageActivity::class.java) } else { - Intent() + Intent(context, FileDisplayActivity::class.java) + }.apply { + putExtra(FileActivity.EXTRA_FILE, operation.file) + putExtra(FileActivity.EXTRA_USER, operation.user) + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP } + } else { + Intent() } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt index 5da4fc1..b03db62 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.download @@ -16,13 +16,16 @@ import android.util.Pair import androidx.core.util.component1 import androidx.core.util.component2 import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.work.Worker +import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.model.WorkerState import com.nextcloud.model.WorkerStateLiveData import com.nextcloud.utils.ForegroundServiceHelper +import com.nextcloud.utils.extensions.getParentIdsOfSubfiles +import com.nextcloud.utils.extensions.getPercent import com.owncloud.android.R import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.ForegroundServiceType @@ -36,11 +39,14 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCo import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.DownloadFileOperation import com.owncloud.android.operations.DownloadType +import com.owncloud.android.ui.events.EventBusFactory +import com.owncloud.android.ui.events.FileDownloadProgressEvent import com.owncloud.android.utils.theme.ViewThemeUtils -import java.security.SecureRandom import java.util.AbstractList import java.util.Optional import java.util.Vector +import java.util.concurrent.ConcurrentHashMap +import kotlin.random.Random @Suppress("LongParameterList", "TooManyFunctions") class FileDownloadWorker( @@ -49,12 +55,15 @@ class FileDownloadWorker( private var localBroadcastManager: LocalBroadcastManager, private val context: Context, params: WorkerParameters -) : Worker(context, params), OnAccountsUpdateListener, OnDatatransferProgressListener { +) : CoroutineWorker(context, params), + OnAccountsUpdateListener, + OnDatatransferProgressListener { companion object { private val TAG = FileDownloadWorker::class.java.simpleName private val pendingDownloads = IndexedForest() + private val pendingFolderDownloads: MutableSet = ConcurrentHashMap.newKeySet() fun cancelOperation(accountName: String, fileId: Long) { pendingDownloads.all.forEach { @@ -62,10 +71,12 @@ class FileDownloadWorker( } } - fun isDownloading(accountName: String, fileId: Long): Boolean { - return pendingDownloads.all.any { it.value?.payload?.isMatching(accountName, fileId) == true } + fun isDownloading(accountName: String, fileId: Long): Boolean = pendingDownloads.all.any { + it.value?.payload?.isMatching(accountName, fileId) == true } + fun isDownloadingFolder(id: Long): Boolean = pendingFolderDownloads.contains(id) + const val FILE_REMOTE_PATH = "FILE_REMOTE_PATH" const val ACCOUNT_NAME = "ACCOUNT_NAME" const val BEHAVIOUR = "BEHAVIOUR" @@ -73,19 +84,14 @@ class FileDownloadWorker( const val ACTIVITY_NAME = "ACTIVITY_NAME" const val PACKAGE_NAME = "PACKAGE_NAME" const val CONFLICT_UPLOAD_ID = "CONFLICT_UPLOAD_ID" - const val EXTRA_DOWNLOAD_RESULT = "EXTRA_DOWNLOAD_RESULT" const val EXTRA_REMOTE_PATH = "EXTRA_REMOTE_PATH" const val EXTRA_LINKED_TO_PATH = "EXTRA_LINKED_TO_PATH" const val EXTRA_ACCOUNT_NAME = "EXTRA_ACCOUNT_NAME" - fun getDownloadAddedMessage(): String { - return FileDownloadWorker::class.java.name + "DOWNLOAD_ADDED" - } + fun getDownloadAddedMessage(): String = FileDownloadWorker::class.java.name + "DOWNLOAD_ADDED" - fun getDownloadFinishMessage(): String { - return FileDownloadWorker::class.java.name + "DOWNLOAD_FINISH" - } + fun getDownloadFinishMessage(): String = FileDownloadWorker::class.java.name + "DOWNLOAD_FINISH" } private var currentDownload: DownloadFileOperation? = null @@ -95,7 +101,7 @@ class FileDownloadWorker( private val intents = FileDownloadIntents(context) private var notificationManager = DownloadNotificationManager( - SecureRandom().nextInt(), + Random.nextInt(), context, viewThemeUtils ) @@ -110,18 +116,17 @@ class FileDownloadWorker( private var downloadError: FileDownloadError? = null - @Suppress("TooGenericExceptionCaught") - override fun doWork(): Result { - return try { - val requestDownloads = getRequestDownloads() - addAccountUpdateListener() + @Suppress("TooGenericExceptionCaught", "ReturnCount") + override suspend fun doWork(): Result { + val foregroundInfo = createWorkerForegroundInfo() + setForeground(foregroundInfo) - val foregroundInfo = ForegroundServiceHelper.createWorkerForegroundInfo( - notificationManager.getId(), - notificationManager.getNotification(), - ForegroundServiceType.DataSync - ) - setForegroundAsync(foregroundInfo) + return try { + setUser() + val remotePath = inputData.keyValueMap[FILE_REMOTE_PATH] as String? ?: return Result.failure() + val ocFile = fileDataStorageManager?.getFileByEncryptedRemotePath(remotePath) ?: return Result.failure() + val requestDownloads = getRequestDownloads(ocFile) + addAccountUpdateListener() requestDownloads.forEach { downloadFile(it) @@ -132,43 +137,43 @@ class FileDownloadWorker( notificationManager.dismissNotification() } - setIdleWorkerState() - Log_OC.e(TAG, "FilesDownloadWorker successfully completed") Result.success() } catch (t: Throwable) { - notificationManager.dismissNotification() notificationManager.showNewNotification(context.getString(R.string.downloader_unexpected_error)) Log_OC.e(TAG, "Error caught at FilesDownloadWorker(): " + t.localizedMessage) - setIdleWorkerState() Result.failure() + } finally { + Log_OC.e(TAG, "FilesDownloadWorker cleanup") + notificationManager.dismissNotification() + setIdleWorkerState() } } - override fun onStopped() { - Log_OC.e(TAG, "FilesDownloadWorker stopped") - - notificationManager.dismissNotification() - setIdleWorkerState() - - super.onStopped() - } + private fun createWorkerForegroundInfo(): ForegroundInfo = ForegroundServiceHelper.createWorkerForegroundInfo( + notificationManager.getId(), + notificationManager.getNotification(), + ForegroundServiceType.DataSync + ) private fun setWorkerState(user: User?) { - WorkerStateLiveData.instance().setWorkState(WorkerState.Download(user, currentDownload)) + WorkerStateLiveData.instance().setWorkState(WorkerState.DownloadStarted(user, currentDownload)) } private fun setIdleWorkerState() { - WorkerStateLiveData.instance().setWorkState(WorkerState.Idle) + WorkerStateLiveData.instance().setWorkState(WorkerState.DownloadFinished(getCurrentFile())) } private fun removePendingDownload(accountName: String?) { pendingDownloads.remove(accountName) } - private fun getRequestDownloads(): AbstractList { - setUser() - val files = getFiles() + private fun getRequestDownloads(ocFile: OCFile): AbstractList { + val files = getFiles(ocFile) + val filesPaths = files.map { it.remotePath } + val parentIdsOfSubFiles = fileDataStorageManager?.getParentIdsOfSubfiles(filesPaths) ?: listOf() + pendingFolderDownloads.addAll(parentIdsOfSubFiles) + val downloadType = getDownloadType() conflictUploadId = inputData.keyValueMap[CONFLICT_UPLOAD_ID] as Long? @@ -221,15 +226,10 @@ class FileDownloadWorker( fileDataStorageManager = FileDataStorageManager(user, context.contentResolver) } - private fun getFiles(): List { - val remotePath = inputData.keyValueMap[FILE_REMOTE_PATH] as String? - val file = fileDataStorageManager?.getFileByEncryptedRemotePath(remotePath) ?: return listOf() - - return if (file.isFolder) { - fileDataStorageManager?.getAllFilesRecursivelyInsideFolder(file) ?: listOf() - } else { - listOf(file) - } + private fun getFiles(file: OCFile): List = if (file.isFolder) { + fileDataStorageManager?.getAllFilesRecursivelyInsideFolder(file) ?: listOf() + } else { + listOf(file) } private fun getDownloadType(): DownloadType? { @@ -267,7 +267,12 @@ class FileDownloadWorker( return } - notifyDownloadStart(currentDownload!!) + lastPercent = 0 + notificationManager.run { + prepareForStart(currentDownload!!) + setContentIntent(intents.detailsIntent(currentDownload!!), PendingIntent.FLAG_IMMUTABLE) + } + var downloadResult: RemoteOperationResult<*>? = null try { val ocAccount = getOCAccountForDownload() @@ -288,15 +293,6 @@ class FileDownloadWorker( } } - private fun notifyDownloadStart(download: DownloadFileOperation) { - lastPercent = 0 - - notificationManager.run { - prepareForStart(download) - setContentIntent(intents.detailsIntent(download), PendingIntent.FLAG_IMMUTABLE) - } - } - @Suppress("DEPRECATION") private fun getOCAccountForDownload(): OwnCloudAccount { val currentDownloadAccount = currentDownload?.user?.toPlatformAccount() @@ -332,6 +328,7 @@ class FileDownloadWorker( currentDownload?.user?.accountName, currentDownload?.remotePath ) + pendingFolderDownloads.remove(currentDownload?.file?.parentId) val downloadResult = result ?: RemoteOperationResult(RuntimeException("Error downloading…")) @@ -350,6 +347,7 @@ class FileDownloadWorker( private fun checkDownloadError(result: RemoteOperationResult<*>) { if (result.isSuccess || downloadError != null) { + notificationManager.dismissNotification() return } @@ -365,6 +363,7 @@ class FileDownloadWorker( FileDownloadError.Cancelled -> { context.getString(R.string.downloader_file_download_cancelled) } + FileDownloadError.Failed -> { context.getString(R.string.downloader_file_download_failed) } @@ -373,10 +372,7 @@ class FileDownloadWorker( notificationManager.showNewNotification(text) } - private fun notifyDownloadResult( - download: DownloadFileOperation, - downloadResult: RemoteOperationResult<*> - ) { + private fun notifyDownloadResult(download: DownloadFileOperation, downloadResult: RemoteOperationResult<*>) { if (downloadResult.isCancelled) { return } @@ -404,6 +400,10 @@ class FileDownloadWorker( } } + @Suppress("MagicNumber") + private val minProgressUpdateInterval = 750 + private var lastUpdateTime = 0L + @Suppress("MagicNumber") override fun onTransferProgress( progressRate: Long, @@ -411,23 +411,25 @@ class FileDownloadWorker( totalToTransfer: Long, filePath: String ) { - val percent: Int = (100.0 * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt() + val percent: Int = downloadProgressListener.getPercent(totalTransferredSoFar, totalToTransfer) + val currentTime = System.currentTimeMillis() - if (percent != lastPercent) { + if (percent != lastPercent && (currentTime - lastUpdateTime) >= minProgressUpdateInterval) { notificationManager.run { - updateDownloadProgress(filePath, percent, totalToTransfer) + updateDownloadProgress(percent, totalToTransfer) } + lastUpdateTime = currentTime } lastPercent = percent + EventBusFactory.downloadProgressEventBus.post(FileDownloadProgressEvent(percent)) } + // CHECK: Is this class still needed after conversion from Foreground Services to Worker? inner class FileDownloadProgressListener : OnDatatransferProgressListener { private val boundListeners: MutableMap = HashMap() - fun isDownloading(user: User?, file: OCFile?): Boolean { - return FileDownloadHelper.instance().isDownloading(user, file) - } + fun isDownloading(user: User?, file: OCFile?): Boolean = FileDownloadHelper.instance().isDownloading(user, file) fun addDataTransferProgressListener(listener: OnDatatransferProgressListener?, file: OCFile?) { if (file == null || listener == null) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt new file mode 100644 index 0000000..b6e19b3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/metadata/MetadataWorker.kt @@ -0,0 +1,83 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.metadata + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import com.nextcloud.client.account.User +import com.nextcloud.utils.extensions.getNonEncryptedSubfolders +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.RefreshFolderOperation +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class MetadataWorker(private val context: Context, params: WorkerParameters, private val user: User) : + CoroutineWorker(context, params) { + + companion object { + private const val TAG = "MetadataWorker" + const val FILE_PATH = "file_path" + } + + @Suppress("DEPRECATION", "ReturnCount") + override suspend fun doWork(): Result { + val storageManager = FileDataStorageManager(user, context.contentResolver) + val filePath = inputData.getString(FILE_PATH) + if (filePath == null) { + Log_OC.e(TAG, "❌ Invalid folder path. Aborting metadata sync. $filePath") + return Result.failure() + } + val currentDir = storageManager.getFileByDecryptedRemotePath(filePath) + if (currentDir == null) { + Log_OC.e(TAG, "❌ Current directory is null. Aborting metadata sync. $filePath") + return Result.failure() + } + Log_OC.d(TAG, "🕒 Starting metadata sync for folder: $filePath") + + // first check current dir + refreshFolder(currentDir, storageManager) + + // then get up-to-date subfolders + val subfolders = storageManager.getNonEncryptedSubfolders(currentDir.fileId, user.accountName) + subfolders.forEach { subFolder -> + refreshFolder(subFolder, storageManager) + } + + Log_OC.d(TAG, "🏁 Metadata sync completed for folder: $filePath") + return Result.success() + } + + @Suppress("DEPRECATION") + private suspend fun refreshFolder(folder: OCFile, storageManager: FileDataStorageManager) = + withContext(Dispatchers.IO) { + Log_OC.d( + TAG, + "📂 eTag check\n" + + " Path: " + folder.remotePath + "\n" + + " eTag: " + folder.etag + "\n" + + " eTagOnServer: " + folder.etagOnServer + ) + if (!folder.isEtagChanged) { + Log_OC.d(TAG, "Skipping ${folder.remotePath}, eTag didn't change") + return@withContext + } + + Log_OC.d(TAG, "⏳ Fetching metadata for: ${folder.remotePath}") + + val operation = RefreshFolderOperation(folder, storageManager, user, context) + val result = operation.execute(user, context) + if (result.isSuccess) { + Log_OC.d(TAG, "✅ Successfully fetched metadata for: ${folder.remotePath}") + } else { + Log_OC.e(TAG, "❌ Failed to fetch metadata for: ${folder.remotePath}") + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt new file mode 100644 index 0000000..e427142 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt @@ -0,0 +1,70 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.notification + +import android.app.Notification +import android.app.NotificationManager +import android.content.Context +import android.graphics.BitmapFactory +import android.os.Handler +import android.os.Looper +import androidx.core.app.NotificationCompat +import com.owncloud.android.R +import com.owncloud.android.utils.theme.ViewThemeUtils + +open class WorkerNotificationManager( + private val id: Int, + private val context: Context, + viewThemeUtils: ViewThemeUtils, + private val tickerId: Int, + channelId: String +) { + var currentOperationTitle: String? = null + + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + var notificationBuilder: NotificationCompat.Builder = + NotificationCompat.Builder(context, channelId).apply { + setTicker(context.getString(tickerId)) + setSmallIcon(R.drawable.notification_icon) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon)) + setStyle(NotificationCompat.BigTextStyle()) + priority = NotificationCompat.PRIORITY_LOW + setSound(null) + setVibrate(null) + setOnlyAlertOnce(true) + setSilent(true) + viewThemeUtils.androidx.themeNotificationCompatBuilder(context, this) + } + + fun showNotification() { + notificationManager.notify(id, notificationBuilder.build()) + } + + @Suppress("MagicNumber") + fun setProgress(percent: Int, progressText: String?, indeterminate: Boolean) { + notificationBuilder.run { + setProgress(100, percent, indeterminate) + setContentTitle(currentOperationTitle) + + progressText?.let { + setContentText(progressText) + } + } + } + + fun dismissNotification(delay: Long = 0) { + Handler(Looper.getMainLooper()).postDelayed({ + notificationManager.cancel(id) + }, delay) + } + + fun getId(): Int = id + + fun getNotification(): Notification = notificationBuilder.build() +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsNotificationManager.kt new file mode 100644 index 0000000..fce69c3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsNotificationManager.kt @@ -0,0 +1,168 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.offlineOperations + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import com.nextcloud.client.database.entity.OfflineOperationEntity +import com.nextcloud.client.jobs.notification.WorkerNotificationManager +import com.nextcloud.client.jobs.offlineOperations.receiver.OfflineOperationReceiver +import com.nextcloud.utils.extensions.getErrorMessage +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.ui.activity.ConflictsResolveActivity +import com.owncloud.android.ui.notifications.NotificationUtils +import com.owncloud.android.utils.theme.ViewThemeUtils + +class OfflineOperationsNotificationManager(private val context: Context, viewThemeUtils: ViewThemeUtils) : + WorkerNotificationManager( + ID, + context, + viewThemeUtils, + tickerId = R.string.offline_operations_worker_notification_manager_ticker, + channelId = NotificationUtils.NOTIFICATION_CHANNEL_OFFLINE_OPERATIONS + ) { + + companion object { + private const val ID = 121 + const val ERROR_ID = 122 + + private const val ONE_HUNDRED_PERCENT = 100 + } + + fun start() { + notificationBuilder.run { + setContentTitle(context.getString(R.string.offline_operations_worker_notification_start_text)) + setProgress(ONE_HUNDRED_PERCENT, 0, false) + } + + showNotification() + } + + fun update(totalOperationSize: Int, currentOperationIndex: Int, filename: String) { + val title = if (totalOperationSize > 1) { + String.format( + context.getString(R.string.offline_operations_worker_progress_text), + currentOperationIndex, + totalOperationSize, + filename + ) + } else { + filename + } + + val progress = (currentOperationIndex * ONE_HUNDRED_PERCENT) / totalOperationSize + + notificationBuilder.run { + setContentTitle(title) + setProgress(ONE_HUNDRED_PERCENT, progress, false) + } + + showNotification() + } + + fun showNewNotification(id: Int?, result: RemoteOperationResult<*>, operation: RemoteOperation<*>) { + val reason = (result to operation).getErrorMessage() + val text = context.getString(R.string.offline_operations_worker_notification_error_text, reason) + val cancelOfflineOperationAction = id?.let { getCancelOfflineOperationAction(it) } + + notificationBuilder.run { + cancelOfflineOperationAction?.let { + addAction(it) + } + setContentTitle(text) + setOngoing(false) + setProgress(0, 0, false) + notificationManager.notify(ERROR_ID, this.build()) + } + } + + fun showConflictNotificationForDeleteOrRemoveOperation(entity: OfflineOperationEntity?) { + val id = entity?.id + if (id == null) { + return + } + + val title = entity.getConflictText(context) + + notificationBuilder + .setProgress(0, 0, false) + .setOngoing(false) + .clearActions() + .setContentTitle(title) + + notificationManager.notify(id, notificationBuilder.build()) + } + + fun showConflictResolveNotification(file: OCFile, entity: OfflineOperationEntity?) { + val path = entity?.path + val id = entity?.id + + if (path == null || id == null) { + return + } + + val resolveConflictAction = getResolveConflictAction(file, id, path) + + val title = entity.getConflictText(context) + + notificationBuilder + .setProgress(0, 0, false) + .setOngoing(false) + .clearActions() + .setContentTitle(title) + .setContentIntent(resolveConflictAction.actionIntent) + .addAction(resolveConflictAction) + + notificationManager.notify(id, notificationBuilder.build()) + } + + private fun getResolveConflictAction(file: OCFile, id: Int, path: String): NotificationCompat.Action { + val intent = ConflictsResolveActivity.createIntent(file, path, context) + val pendingIntent = PendingIntent.getActivity( + context, + id, + intent, + PendingIntent.FLAG_IMMUTABLE + ) + + return NotificationCompat.Action( + R.drawable.ic_cloud_upload, + context.getString(R.string.upload_list_resolve_conflict), + pendingIntent + ) + } + + private fun getCancelOfflineOperationAction(id: Int): NotificationCompat.Action { + val intent = Intent(context, OfflineOperationReceiver::class.java).apply { + putExtra(OfflineOperationReceiver.ID, id) + } + + val pendingIntent = PendingIntent.getBroadcast( + context, + id, + intent, + PendingIntent.FLAG_IMMUTABLE + ) + + return NotificationCompat.Action( + R.drawable.ic_delete, + context.getString(R.string.common_cancel), + pendingIntent + ) + } + + fun dismissNotification(id: Int?) { + if (id == null) return + notificationManager.cancel(id) + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt new file mode 100644 index 0000000..9094918 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt @@ -0,0 +1,301 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.offlineOperations + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import com.nextcloud.client.account.User +import com.nextcloud.client.database.entity.OfflineOperationEntity +import com.nextcloud.client.jobs.offlineOperations.repository.OfflineOperationsRepository +import com.nextcloud.client.network.ClientFactoryImpl +import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.model.OfflineOperationType +import com.nextcloud.model.WorkerState +import com.nextcloud.model.WorkerStateLiveData +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation +import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation +import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation +import com.owncloud.android.lib.resources.files.model.RemoteFile +import com.owncloud.android.operations.CreateFolderOperation +import com.owncloud.android.operations.RemoveFileOperation +import com.owncloud.android.operations.RenameFileOperation +import com.owncloud.android.utils.MimeTypeUtil +import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +private typealias OfflineOperationResult = Pair?, RemoteOperation<*>?>? + +class OfflineOperationsWorker( + private val user: User, + private val context: Context, + private val connectivityService: ConnectivityService, + viewThemeUtils: ViewThemeUtils, + params: WorkerParameters +) : CoroutineWorker(context, params) { + + companion object { + private val TAG = OfflineOperationsWorker::class.java.simpleName + const val JOB_NAME = "JOB_NAME" + + private const val ONE_SECOND = 1000L + } + + private val fileDataStorageManager = FileDataStorageManager(user, context.contentResolver) + private val clientFactory = ClientFactoryImpl(context) + private val notificationManager = OfflineOperationsNotificationManager(context, viewThemeUtils) + private var repository = OfflineOperationsRepository(fileDataStorageManager) + + @Suppress("TooGenericExceptionCaught") + override suspend fun doWork(): Result = withContext(Dispatchers.IO) { + try { + val jobName = inputData.getString(JOB_NAME) + Log_OC.d(TAG, "[$jobName] OfflineOperationsWorker started for user: ${user.accountName}") + + // check network connection + if (!isNetworkAndServerAvailable()) { + Log_OC.w(TAG, "⚠️ No internet/server connection. Retrying later...") + return@withContext Result.retry() + } + + // check offline operations + val operations = fileDataStorageManager.offlineOperationDao.getAll() + if (operations.isEmpty()) { + Log_OC.d(TAG, "Skipping, no offline operation found") + return@withContext Result.success() + } + + // process offline operations + notificationManager.start() + val client = clientFactory.create(user) + processOperations(operations, client) + + // finish + WorkerStateLiveData.instance().setWorkState(WorkerState.OfflineOperationsCompleted) + Log_OC.d(TAG, "🏁 Worker finished with result") + return@withContext Result.success() + } catch (e: Exception) { + Log_OC.e(TAG, "💥 ProcessOperations failed: ${e.message}") + return@withContext Result.failure() + } finally { + notificationManager.dismissNotification() + } + } + + // region Handle offline operations + @Suppress("TooGenericExceptionCaught") + private suspend fun processOperations(operations: List, client: OwnCloudClient) { + val totalOperationSize = operations.size + operations.forEachIndexed { index, operation -> + try { + Log_OC.d(TAG, "Processing operation, path: ${operation.path}") + val result = executeOperation(operation, client) + handleResult(operation, totalOperationSize, index, result) + } catch (e: Exception) { + Log_OC.e(TAG, "💥 Exception while processing operation id=${operation.id}: ${e.message}") + } + } + } + + private fun handleResult( + operation: OfflineOperationEntity, + totalOperations: Int, + currentSuccessfulOperationIndex: Int, + result: OfflineOperationResult + ) { + val operationResult = result?.first ?: return + val logMessage = if (operationResult.isSuccess) "Operation completed" else "Operation failed" + Log_OC.d(TAG, "$logMessage filename: ${operation.filename}, type: ${operation.type}") + + return if (result.first?.isSuccess == true) { + handleSuccessResult(operation, totalOperations, currentSuccessfulOperationIndex) + } else { + handleErrorResult(operation.id, result) + } + } + + private fun handleSuccessResult( + operation: OfflineOperationEntity, + totalOperations: Int, + currentSuccessfulOperationIndex: Int + ) { + if (operation.type is OfflineOperationType.RemoveFile) { + val operationType = operation.type as OfflineOperationType.RemoveFile + fileDataStorageManager.getFileByDecryptedRemotePath(operationType.path)?.let { ocFile -> + repository.deleteOperation(ocFile) + } + } else { + repository.updateNextOperations(operation) + } + + fileDataStorageManager.offlineOperationDao.delete(operation) + notificationManager.update(totalOperations, currentSuccessfulOperationIndex + 1, operation.filename ?: "") + } + + private fun handleErrorResult(id: Int?, result: OfflineOperationResult) { + val operationResult = result?.first ?: return + val operation = result.second ?: return + Log_OC.e(TAG, "❌ Operation failed [id=$id]: code=${operationResult.code}, message=${operationResult.message}") + val excludedErrorCodes = + listOf(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS, RemoteOperationResult.ResultCode.LOCKED) + + if (!excludedErrorCodes.contains(operationResult.code)) { + notificationManager.showNewNotification(id, operationResult, operation) + } else { + Log_OC.d(TAG, "ℹ️ Ignored error: ${operationResult.code}") + } + } + // endregion + + private suspend fun isNetworkAndServerAvailable(): Boolean = suspendCoroutine { continuation -> + connectivityService.isNetworkAndServerAvailable { result -> + continuation.resume(result) + } + } + + // region Operation Execution + @Suppress("ComplexCondition", "LongMethod") + private suspend fun executeOperation( + operation: OfflineOperationEntity, + client: OwnCloudClient + ): OfflineOperationResult? = withContext(Dispatchers.IO) { + var path = (operation.path) + if (path == null) { + Log_OC.w(TAG, "⚠️ Skipped: path is null for operation id=${operation.id}") + return@withContext null + } + + if (operation.type is OfflineOperationType.CreateFile && path.endsWith(OCFile.PATH_SEPARATOR)) { + Log_OC.w( + TAG, + "Create file operation should not ends with path separator removing suffix, " + + "operation id=${operation.id}" + ) + path = path.removeSuffix(OCFile.PATH_SEPARATOR) + } + + val remoteFile = getRemoteFile(path) + val ocFile = fileDataStorageManager.getFileByDecryptedRemotePath(path) + + if (remoteFile != null && ocFile != null && isFileChanged(remoteFile, ocFile)) { + Log_OC.w(TAG, "⚠️ Conflict detected: File already exists on server. Skipping operation id=${operation.id}") + + if (operation.isRenameOrRemove()) { + Log_OC.d(TAG, "🗑 Removing conflicting rename/remove operation id=${operation.id}") + fileDataStorageManager.offlineOperationDao.delete(operation) + notificationManager.showConflictNotificationForDeleteOrRemoveOperation(operation) + } else { + Log_OC.d(TAG, "📌 Showing conflict resolution for operation id=${operation.id}") + notificationManager.showConflictResolveNotification(ocFile, operation) + } + + return@withContext null + } + + if (operation.isRenameOrRemove() && ocFile == null) { + Log_OC.d(TAG, "Skipping, attempting to delete or rename non-existing file") + fileDataStorageManager.offlineOperationDao.delete(operation) + return@withContext null + } + + if (operation.isCreate() && remoteFile != null && ocFile != null && !isFileChanged(remoteFile, ocFile)) { + Log_OC.d(TAG, "Skipping, attempting to create same file creation") + fileDataStorageManager.offlineOperationDao.delete(operation) + return@withContext null + } + + return@withContext when (val type = operation.type) { + is OfflineOperationType.CreateFolder -> { + Log_OC.d(TAG, "📂 Creating folder at ${type.path}") + createFolder(operation, client) + } + is OfflineOperationType.CreateFile -> { + Log_OC.d(TAG, "📤 Uploading file: local=${type.localPath} → remote=${type.remotePath}") + createFile(operation, client) + } + is OfflineOperationType.RenameFile -> { + Log_OC.d(TAG, "✏️ Renaming ${operation.path} → ${type.newName}") + renameFile(operation, client) + } + is OfflineOperationType.RemoveFile -> { + Log_OC.d(TAG, "🗑 Removing file: ${operation.path}") + ocFile?.let { removeFile(it, client) } + } + else -> { + Log_OC.d(TAG, "⚠️ Unsupported operation type: $type") + null + } + } + } + + @Suppress("DEPRECATION") + private fun createFolder(operation: OfflineOperationEntity, client: OwnCloudClient): OfflineOperationResult { + val operationType = (operation.type as OfflineOperationType.CreateFolder) + val createFolderOperation = CreateFolderOperation(operationType.path, user, context, fileDataStorageManager) + return createFolderOperation.execute(client) to createFolderOperation + } + + @Suppress("DEPRECATION") + private fun createFile(operation: OfflineOperationEntity, client: OwnCloudClient): OfflineOperationResult { + val operationType = (operation.type as OfflineOperationType.CreateFile) + val lastModificationDate = System.currentTimeMillis() / ONE_SECOND + val createFileOperation = UploadFileRemoteOperation( + operationType.localPath, + operationType.remotePath, + operationType.mimeType, + "", + operation.modifiedAt ?: lastModificationDate, + operation.createdAt ?: System.currentTimeMillis(), + true + ) + return createFileOperation.execute(client) to createFileOperation + } + + @Suppress("DEPRECATION") + private fun renameFile(operation: OfflineOperationEntity, client: OwnCloudClient): OfflineOperationResult { + val operationType = (operation.type as OfflineOperationType.RenameFile) + val renameFileOperation = RenameFileOperation(operation.path, operationType.newName, fileDataStorageManager) + return renameFileOperation.execute(client) to renameFileOperation + } + + @Suppress("DEPRECATION") + private fun removeFile(ocFile: OCFile, client: OwnCloudClient): OfflineOperationResult { + val removeFileOperation = RemoveFileOperation(ocFile, false, user, true, context, fileDataStorageManager) + return removeFileOperation.execute(client) to removeFileOperation + } + // endregion + + @Suppress("DEPRECATION") + private fun getRemoteFile(remotePath: String): RemoteFile? { + val mimeType = MimeTypeUtil.getMimeTypeFromPath(remotePath) + val isFolder = MimeTypeUtil.isFolder(mimeType) + val client = ClientFactoryImpl(context).create(user) + val result = if (isFolder) { + ReadFolderRemoteOperation(remotePath).execute(client) + } else { + ReadFileRemoteOperation(remotePath).execute(client) + } + + return if (result.isSuccess) { + result.data[0] as? RemoteFile + } else { + null + } + } + + private fun isFileChanged(remoteFile: RemoteFile, ocFile: OCFile): Boolean = remoteFile.etag != ocFile.etagOnServer +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/receiver/OfflineOperationReceiver.kt b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/receiver/OfflineOperationReceiver.kt new file mode 100644 index 0000000..54179ed --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/receiver/OfflineOperationReceiver.kt @@ -0,0 +1,41 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.offlineOperations.receiver + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsNotificationManager +import com.owncloud.android.MainApp +import com.owncloud.android.datamodel.FileDataStorageManager +import javax.inject.Inject + +class OfflineOperationReceiver : BroadcastReceiver() { + companion object { + const val ID = "id" + } + + @Inject + lateinit var storageManager: FileDataStorageManager + + override fun onReceive(context: Context, intent: Intent) { + MainApp.getAppComponent().inject(this) + + val id = intent.getIntExtra(ID, -1) + if (id == -1) { + return + } + + storageManager.offlineOperationDao.deleteById(id) + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel( + OfflineOperationsNotificationManager.ERROR_ID + ) + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepository.kt b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepository.kt new file mode 100644 index 0000000..86170e4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepository.kt @@ -0,0 +1,114 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.offlineOperations.repository + +import com.nextcloud.client.database.entity.OfflineOperationEntity +import com.nextcloud.model.OfflineOperationType +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.utils.MimeType +import com.owncloud.android.utils.MimeTypeUtil + +class OfflineOperationsRepository(private val fileDataStorageManager: FileDataStorageManager) : + OfflineOperationsRepositoryType { + + private val dao = fileDataStorageManager.offlineOperationDao + private val pathSeparator = '/' + + @Suppress("NestedBlockDepth") + override fun getAllSubEntities(fileId: Long): List { + val result = mutableListOf() + val queue = ArrayDeque() + queue.add(fileId) + val processedIds = mutableSetOf() + + while (queue.isNotEmpty()) { + val currentFileId = queue.removeFirst() + if (currentFileId in processedIds || currentFileId == 1L) continue + + processedIds.add(currentFileId) + + val subDirectories = dao.getSubEntitiesByParentOCFileId(currentFileId) + result.addAll(subDirectories) + + subDirectories.forEach { + val ocFile = fileDataStorageManager.getFileByDecryptedRemotePath(it.path) + ocFile?.fileId?.let { newFileId -> + if (newFileId != 1L && newFileId !in processedIds) { + queue.add(newFileId) + } + } + } + } + + return result + } + + override fun deleteOperation(file: OCFile) { + if (file.isFolder) { + getAllSubEntities(file.fileId).forEach { + dao.delete(it) + } + } + + file.decryptedRemotePath?.let { + dao.deleteByPath(it) + } + + fileDataStorageManager.removeFile(file, true, true) + } + + override fun updateNextOperations(operation: OfflineOperationEntity) { + val ocFile = fileDataStorageManager.getFileByDecryptedRemotePath(operation.path) + val fileId = ocFile?.fileId ?: return + + getAllSubEntities(fileId) + .mapNotNull { nextOperation -> + nextOperation.parentOCFileId?.let { parentId -> + fileDataStorageManager.getFileById(parentId)?.let { ocFile -> + ocFile.decryptedRemotePath?.let { updatedPath -> + val newPath = updatedPath + nextOperation.filename + pathSeparator + + if (newPath != nextOperation.path) { + nextOperation.apply { + type = when (type) { + is OfflineOperationType.CreateFile -> + (type as OfflineOperationType.CreateFile).copy( + remotePath = newPath + ) + + is OfflineOperationType.CreateFolder -> + (type as OfflineOperationType.CreateFolder).copy( + path = newPath + ) + + else -> type + } + path = newPath + } + } else { + null + } + } + } + } + } + .forEach { dao.update(it) } + } + + override fun convertToOCFiles(fileId: Long): List = + dao.getSubEntitiesByParentOCFileId(fileId).map { entity -> + OCFile(entity.path).apply { + mimeType = if (entity.type is OfflineOperationType.CreateFolder) { + MimeType.DIRECTORY + } else { + MimeTypeUtil.getMimeTypeFromPath(entity.path) + } + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepositoryType.kt b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepositoryType.kt new file mode 100644 index 0000000..b650909 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/repository/OfflineOperationsRepositoryType.kt @@ -0,0 +1,18 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.offlineOperations.repository + +import com.nextcloud.client.database.entity.OfflineOperationEntity +import com.owncloud.android.datamodel.OCFile + +interface OfflineOperationsRepositoryType { + fun getAllSubEntities(fileId: Long): List + fun deleteOperation(file: OCFile) + fun updateNextOperations(operation: OfflineOperationEntity) + fun convertToOCFiles(fileId: Long): List +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/operation/FileOperationHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/operation/FileOperationHelper.kt new file mode 100644 index 0000000..6cbcb77 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/operation/FileOperationHelper.kt @@ -0,0 +1,66 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.operation + +import android.content.Context +import com.nextcloud.client.account.User +import com.nextcloud.utils.extensions.getErrorMessage +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.RemoveFileOperation +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.withContext + +class FileOperationHelper( + private val user: User, + private val context: Context, + private val fileDataStorageManager: FileDataStorageManager +) { + companion object { + private val TAG = FileOperationHelper::class.java.simpleName + } + + @Suppress("TooGenericExceptionCaught", "Deprecation") + suspend fun removeFile( + file: OCFile, + onlyLocalCopy: Boolean, + inBackground: Boolean, + client: OwnCloudClient + ): Boolean { + return withContext(Dispatchers.IO) { + try { + val operation = async { + RemoveFileOperation( + file, + onlyLocalCopy, + user, + inBackground, + context, + fileDataStorageManager + ) + } + val operationResult = operation.await() + val result = operationResult.execute(client) + + return@withContext if (result.isSuccess) { + true + } else { + val reason = (result to operationResult).getErrorMessage() + Log_OC.e(TAG, "Error occurred while removing file: $reason") + false + } + } catch (e: Exception) { + Log_OC.e(TAG, "Error occurred while removing file: $e") + false + } + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt index 971a76e..cfe0d0d 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.transfer @@ -41,27 +41,23 @@ class FileTransferService : LifecycleService() { const val EXTRA_REQUEST = "request" const val EXTRA_USER = "user" - fun createBindIntent(context: Context, user: User): Intent { - return Intent(context, FileTransferService::class.java).apply { + fun createBindIntent(context: Context, user: User): Intent = + Intent(context, FileTransferService::class.java).apply { putExtra(EXTRA_USER, user) } - } - fun createTransferRequestIntent(context: Context, request: Request): Intent { - return Intent(context, FileTransferService::class.java).apply { + fun createTransferRequestIntent(context: Context, request: Request): Intent = + Intent(context, FileTransferService::class.java).apply { action = ACTION_TRANSFER putExtra(EXTRA_REQUEST, request) } - } } /** * Binder forwards [TransferManager] API calls to selected instance of downloader. */ - class Binder( - downloader: TransferManagerImpl, - service: FileTransferService - ) : LocalBinder(service), + class Binder(downloader: TransferManagerImpl, service: FileTransferService) : + LocalBinder(service), TransferManager by downloader @Inject diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt index 8a3fa28..b3ecdae 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.transfer diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt index d728fda..ccb688d 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.transfer @@ -19,11 +19,7 @@ interface TransferManager { /** * Snapshot of transfer manager status. All data is immutable and can be safely shared. */ - data class Status( - val pending: List, - val running: List, - val completed: List - ) { + data class Status(val pending: List, val running: List, val completed: List) { companion object { val EMPTY = Status(emptyList(), emptyList(), emptyList()) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt index 34bc9af..d5d97c3 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.transfer @@ -16,10 +16,9 @@ import com.nextcloud.client.files.Request import com.owncloud.android.datamodel.OCFile import java.util.UUID -class TransferManagerConnection( - context: Context, - val user: User -) : LocalConnection(context), TransferManager { +class TransferManagerConnection(context: Context, val user: User) : + LocalConnection(context), + TransferManager { private var transferListeners: MutableSet<(Transfer) -> Unit> = mutableSetOf() private var statusListeners: MutableSet<(TransferManager.Status) -> Unit> = mutableSetOf() @@ -64,9 +63,7 @@ class TransferManagerConnection( binder?.removeStatusListener(listener) } - override fun createBindIntent(): Intent { - return FileTransferService.createBindIntent(context, user) - } + override fun createBindIntent(): Intent = FileTransferService.createBindIntent(context, user) override fun onBound(binder: IBinder) { super.onBound(binder) diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt index 44defb1..45008fe 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.transfer @@ -118,8 +118,8 @@ class TransferManagerImpl( } } - private fun createDownloadTask(request: DownloadRequest): TaskFunction { - return if (request.test) { + private fun createDownloadTask(request: DownloadRequest): TaskFunction = + if (request.test) { { progress: OnProgressCallback, isCancelled: IsCancelled -> testDownloadTask(request.file, progress, isCancelled) } @@ -130,25 +130,22 @@ class TransferManagerImpl( } wrapper } - } - private fun createUploadTask(request: UploadRequest): TaskFunction { - return if (request.test) { - { progress: OnProgressCallback, isCancelled: IsCancelled -> - val file = UploadFileOperation.obtainNewOCFileToUpload( - request.upload.remotePath, - request.upload.localPath, - request.upload.mimeType - ) - testUploadTask(file, progress, isCancelled) - } - } else { - val uploadTask = uploadTaskFactory.create() - val wrapper: TaskFunction = { _: ((Int) -> Unit), _ -> - uploadTask.upload(request.user, request.upload) - } - wrapper + private fun createUploadTask(request: UploadRequest): TaskFunction = if (request.test) { + { progress: OnProgressCallback, isCancelled: IsCancelled -> + val file = UploadFileOperation.obtainNewOCFileToUpload( + request.upload.remotePath, + request.upload.localPath, + request.upload.mimeType + ) + testUploadTask(file, progress, isCancelled) } + } else { + val uploadTask = uploadTaskFactory.create() + val wrapper: TaskFunction = { _: ((Int) -> Unit), _ -> + uploadTask.upload(request.user, request.upload) + } + wrapper } private fun onTransferUpdate(transfer: Transfer) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferState.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferState.kt index 596e51a..5f6b39c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferState.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferState.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.transfer diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadBroadcastReceiver.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadBroadcastReceiver.kt new file mode 100644 index 0000000..eca87bf --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadBroadcastReceiver.kt @@ -0,0 +1,48 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.upload + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.owncloud.android.MainApp +import com.owncloud.android.datamodel.UploadsStorageManager +import com.owncloud.android.ui.notifications.NotificationUtils +import javax.inject.Inject + +class FileUploadBroadcastReceiver : BroadcastReceiver() { + + @Inject + lateinit var uploadsStorageManager: UploadsStorageManager + + companion object { + const val UPLOAD_ID = "UPLOAD_ID" + const val REMOTE_PATH = "REMOTE_PATH" + const val STORAGE_PATH = "STORAGE_PATH" + } + + @Suppress("ReturnCount") + override fun onReceive(context: Context, intent: Intent) { + MainApp.getAppComponent().inject(this) + + val remotePath = intent.getStringExtra(REMOTE_PATH) ?: return + val storagePath = intent.getStringExtra(STORAGE_PATH) ?: return + val uploadId = intent.getLongExtra(UPLOAD_ID, -1L) + if (uploadId == -1L) { + return + } + + uploadsStorageManager.removeUpload(uploadId) + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel( + NotificationUtils.createUploadNotificationTag(remotePath, storagePath), + FileUploadWorker.NOTIFICATION_ERROR_ID + ) + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index fc31abb..99c5bb8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -1,36 +1,51 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload +import android.app.Activity import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.device.BatteryStatus import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.currentUploadFileOperation +import com.nextcloud.client.network.Connectivity import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.utils.extensions.getUploadIds import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus import com.owncloud.android.db.OCUpload import com.owncloud.android.db.UploadResult import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.network.OnDatatransferProgressListener import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation import com.owncloud.android.lib.resources.files.model.RemoteFile +import com.owncloud.android.operations.RemoveFileOperation +import com.owncloud.android.operations.UploadFileOperation +import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.FileUtil +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.io.File -import java.util.Optional +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ExecutionException +import java.util.concurrent.Semaphore import javax.inject.Inject @Suppress("TooManyFunctions") @@ -45,6 +60,11 @@ class FileUploadHelper { @Inject lateinit var uploadsStorageManager: UploadsStorageManager + @Inject + lateinit var fileStorageManager: FileDataStorageManager + + private val ioScope = CoroutineScope(Dispatchers.IO) + init { MainApp.getAppComponent().inject(this) } @@ -59,15 +79,13 @@ class FileUploadHelper { private var instance: FileUploadHelper? = null - fun instance(): FileUploadHelper { - return instance ?: synchronized(this) { - instance ?: FileUploadHelper().also { instance = it } - } + private val retryFailedUploadsSemaphore = Semaphore(1) + + fun instance(): FileUploadHelper = instance ?: synchronized(this) { + instance ?: FileUploadHelper().also { instance = it } } - fun buildRemoteName(accountName: String, remotePath: String): String { - return accountName + remotePath - } + fun buildRemoteName(accountName: String, remotePath: String): String = accountName + remotePath } fun retryFailedUploads( @@ -76,19 +94,27 @@ class FileUploadHelper { accountManager: UserAccountManager, powerManagementService: PowerManagementService ) { - val failedUploads = uploadsStorageManager.failedUploads - if (failedUploads == null || failedUploads.isEmpty()) { - Log_OC.d(TAG, "Failed uploads are empty or null") - return - } + if (retryFailedUploadsSemaphore.tryAcquire()) { + try { + val failedUploads = uploadsStorageManager.failedUploads + if (failedUploads == null || failedUploads.isEmpty()) { + Log_OC.d(TAG, "Failed uploads are empty or null") + return + } - retryUploads( - uploadsStorageManager, - connectivityService, - accountManager, - powerManagementService, - failedUploads - ) + retryUploads( + uploadsStorageManager, + connectivityService, + accountManager, + powerManagementService, + failedUploads + ) + } finally { + retryFailedUploadsSemaphore.release() + } + } else { + Log_OC.d(TAG, "Skip retryFailedUploads since it is already running") + } } fun retryCancelledUploads( @@ -120,37 +146,54 @@ class FileUploadHelper { failedUploads: Array ): Boolean { var showNotExistMessage = false - val (gotNetwork, _, gotWifi) = connectivityService.connectivity + val isOnline = checkConnectivity(connectivityService) + val connectivity = connectivityService.connectivity val batteryStatus = powerManagementService.battery - val charging = batteryStatus.isCharging || batteryStatus.isFull - val isPowerSaving = powerManagementService.isPowerSavingEnabled - var uploadUser = Optional.empty() + val accountNames = accountManager.accounts.filter { account -> + accountManager.getUser(account.name).isPresent + }.map { account -> + account.name + }.toHashSet() for (failedUpload in failedUploads) { - // 1. extract failed upload owner account and cache it between loops (expensive query) - if (!uploadUser.isPresent || !uploadUser.get().nameEquals(failedUpload.accountName)) { - uploadUser = accountManager.getUser(failedUpload.accountName) + if (!accountNames.contains(failedUpload.accountName)) { + uploadsStorageManager.removeUpload(failedUpload) + continue } - val isDeleted = !File(failedUpload.localPath).exists() - if (isDeleted) { - showNotExistMessage = true - // 2A. for deleted files, mark as permanently failed - if (failedUpload.lastResult != UploadResult.FILE_NOT_FOUND) { - failedUpload.lastResult = UploadResult.FILE_NOT_FOUND + val uploadResult = + checkUploadConditions(failedUpload, connectivity, batteryStatus, powerManagementService, isOnline) + + if (uploadResult != UploadResult.UPLOADED) { + if (failedUpload.lastResult != uploadResult) { + // Setting Upload status else cancelled uploads will behave wrong, when retrying + // Needs to happen first since lastResult wil be overwritten by setter + failedUpload.uploadStatus = UploadStatus.UPLOAD_FAILED + + failedUpload.lastResult = uploadResult uploadsStorageManager.updateUpload(failedUpload) } - } else if (!isPowerSaving && gotNetwork && - canUploadBeRetried(failedUpload, gotWifi, charging) && !connectivityService.isInternetWalled - ) { - // 2B. for existing local files, try restarting it if possible - retryUpload(failedUpload, uploadUser.get()) + if (uploadResult == UploadResult.FILE_NOT_FOUND) { + showNotExistMessage = true + } + continue + } + + failedUpload.uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS + uploadsStorageManager.updateUpload(failedUpload) + } + + accountNames.forEach { accountName -> + val user = accountManager.getUser(accountName) + if (user.isPresent) { + backgroundJobManager.startFilesUploadJob(user.get(), failedUploads.getUploadIds(), false) } } return showNotExistMessage } + @JvmOverloads @Suppress("LongParameterList") fun uploadNewFiles( user: User, @@ -161,7 +204,8 @@ class FileUploadHelper { createdBy: Int, requiresWifi: Boolean, requiresCharging: Boolean, - nameCollisionPolicy: NameCollisionPolicy + nameCollisionPolicy: NameCollisionPolicy, + showSameFileAlreadyExistsNotification: Boolean = true ) { val uploads = localPaths.mapIndexed { index, localPath -> OCUpload(localPath, remotePaths[index], user.accountName).apply { @@ -175,7 +219,7 @@ class FileUploadHelper { } } uploadsStorageManager.storeUploads(uploads) - backgroundJobManager.startFilesUploadJob(user) + backgroundJobManager.startFilesUploadJob(user, uploads.getUploadIds(), showSameFileAlreadyExistsNotification) } fun removeFileUpload(remotePath: String, accountName: String) { @@ -185,25 +229,42 @@ class FileUploadHelper { // need to update now table in mUploadsStorageManager, // since the operation will not get to be run by FileUploader#uploadFile uploadsStorageManager.removeUpload(accountName, remotePath) - - cancelAndRestartUploadJob(user) + val uploadIds = uploadsStorageManager.getCurrentUploadIds(user.accountName) + cancelAndRestartUploadJob(user, uploadIds) } catch (e: NoSuchElementException) { - Log_OC.e(TAG, "Error cancelling current upload because user does not exist!") + Log_OC.e(TAG, "Error cancelling current upload because user does not exist!: " + e.message) } } fun cancelFileUpload(remotePath: String, accountName: String) { - uploadsStorageManager.getUploadByRemotePath(remotePath).run { - removeFileUpload(remotePath, accountName) - uploadStatus = UploadStatus.UPLOAD_CANCELLED - uploadsStorageManager.storeUpload(this) + ioScope.launch { + val upload = uploadsStorageManager.getUploadByRemotePath(remotePath) + if (upload != null) { + cancelFileUploads(listOf(upload), accountName) + } else { + Log_OC.e(TAG, "Error cancelling current upload because upload does not exist!") + } } } - fun cancelAndRestartUploadJob(user: User) { + fun cancelFileUploads(uploads: List, accountName: String) { + for (upload in uploads) { + upload.uploadStatus = UploadStatus.UPLOAD_CANCELLED + uploadsStorageManager.updateUpload(upload) + } + + try { + val user = accountManager.getUser(accountName).get() + cancelAndRestartUploadJob(user, uploads.getUploadIds()) + } catch (e: NoSuchElementException) { + Log_OC.e(TAG, "Error restarting upload job because user does not exist!: " + e.message) + } + } + + fun cancelAndRestartUploadJob(user: User, uploadIds: LongArray) { backgroundJobManager.run { cancelFilesUploadJob(user) - startFilesUploadJob(user) + startFilesUploadJob(user, uploadIds, false) } } @@ -213,15 +274,67 @@ class FileUploadHelper { return false } - val upload: OCUpload = uploadsStorageManager.getUploadByRemotePath(file.remotePath) ?: return false - return upload.uploadStatus == UploadStatus.UPLOAD_IN_PROGRESS + val uploadCompletableFuture = CompletableFuture.supplyAsync { + uploadsStorageManager.getUploadByRemotePath(file.remotePath) + } + return try { + val upload = uploadCompletableFuture.get() + if (upload != null) { + upload.uploadStatus == UploadStatus.UPLOAD_IN_PROGRESS + } else { + false + } + } catch (e: ExecutionException) { + false + } catch (e: InterruptedException) { + false + } } - private fun canUploadBeRetried(upload: OCUpload, gotWifi: Boolean, isCharging: Boolean): Boolean { - val file = File(upload.localPath) - val needsWifi = upload.isUseWifiOnly - val needsCharging = upload.isWhileChargingOnly - return file.exists() && (!needsWifi || gotWifi) && (!needsCharging || isCharging) + private fun checkConnectivity(connectivityService: ConnectivityService): Boolean { + // check that connection isn't walled off and that the server is reachable + return connectivityService.getConnectivity().isConnected && !connectivityService.isInternetWalled() + } + + /** + * Dupe of [UploadFileOperation.checkConditions], needed to check if the upload should even be scheduled + * @return [UploadResult.UPLOADED] if the upload should be scheduled, otherwise the reason why it shouldn't + */ + private fun checkUploadConditions( + upload: OCUpload, + connectivity: Connectivity, + battery: BatteryStatus, + powerManagementService: PowerManagementService, + hasGeneralConnection: Boolean + ): UploadResult { + var conditions = UploadResult.UPLOADED + + // check that internet is available + if (!hasGeneralConnection) { + conditions = UploadResult.NETWORK_CONNECTION + } + + // check that local file exists; skip the upload otherwise + if (!File(upload.localPath).exists()) { + conditions = UploadResult.FILE_NOT_FOUND + } + + // check that connectivity conditions are met; delay upload otherwise + if (upload.isUseWifiOnly && (!connectivity.isWifi || connectivity.isMetered)) { + conditions = UploadResult.DELAYED_FOR_WIFI + } + + // check if charging conditions are met; delay upload otherwise + if (upload.isWhileChargingOnly && !battery.isCharging && !battery.isFull) { + conditions = UploadResult.DELAYED_FOR_CHARGING + } + + // check that device is not in power save mode; delay upload otherwise + if (powerManagementService.isPowerSavingEnabled) { + conditions = UploadResult.DELAYED_IN_POWER_SAVE_MODE + } + + return conditions } @Suppress("ReturnCount") @@ -266,7 +379,43 @@ class FileUploadHelper { } } uploadsStorageManager.storeUploads(uploads) - backgroundJobManager.startFilesUploadJob(user) + val uploadIds: LongArray = uploads.filterNotNull().map { it.uploadId }.toLongArray() + backgroundJobManager.startFilesUploadJob(user, uploadIds, true) + } + + /** + * Removes any existing file in the same directory that has the same name as the provided new file. + * + * This function checks the parent directory of the given `newFile` for any file with the same name. + * If such a file is found, it is removed using the `RemoveFileOperation`. + * + * @param duplicatedFile File to be deleted + * @param client Needed for executing RemoveFileOperation + * @param user Needed for creating client + */ + fun removeDuplicatedFile(duplicatedFile: OCFile, client: OwnCloudClient, user: User, onCompleted: () -> Unit) { + val job = CoroutineScope(Dispatchers.IO) + + job.launch { + val removeFileOperation = RemoveFileOperation( + duplicatedFile, + false, + user, + true, + MainApp.getAppContext(), + fileStorageManager + ) + + val result = removeFileOperation.execute(client) + + if (result.isSuccess) { + Log_OC.d(TAG, "Replaced file successfully removed") + + launch(Dispatchers.Main) { + onCompleted() + } + } + } } fun retryUpload(upload: OCUpload, user: User) { @@ -275,25 +424,20 @@ class FileUploadHelper { upload.uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS uploadsStorageManager.updateUpload(upload) - backgroundJobManager.startFilesUploadJob(user) + backgroundJobManager.startFilesUploadJob(user, longArrayOf(upload.uploadId), false) } fun cancel(accountName: String) { uploadsStorageManager.removeUploads(accountName) - cancelAndRestartUploadJob(accountManager.getUser(accountName).get()) + val uploadIds = uploadsStorageManager.getCurrentUploadIds(accountName) + cancelAndRestartUploadJob(accountManager.getUser(accountName).get(), uploadIds) } - fun addUploadTransferProgressListener( - listener: OnDatatransferProgressListener, - targetKey: String - ) { + fun addUploadTransferProgressListener(listener: OnDatatransferProgressListener, targetKey: String) { mBoundListeners[targetKey] = listener } - fun removeUploadTransferProgressListener( - listener: OnDatatransferProgressListener, - targetKey: String - ) { + fun removeUploadTransferProgressListener(listener: OnDatatransferProgressListener, targetKey: String) { if (mBoundListeners[targetKey] === listener) { mBoundListeners.remove(targetKey) } @@ -318,6 +462,14 @@ class FileUploadHelper { return false } + fun showFileUploadLimitMessage(activity: Activity) { + val message = activity.resources.getQuantityString( + R.plurals.file_upload_limit_message, + MAX_FILE_COUNT + ) + DisplayUtils.showSnackMessage(activity, message) + } + class UploadNotificationActionReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val accountName = intent.getStringExtra(FileUploadWorker.EXTRA_ACCOUNT_NAME) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index 80cb01e..37dd12f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload @@ -21,11 +21,13 @@ import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.model.WorkerState import com.nextcloud.model.WorkerStateLiveData +import com.nextcloud.utils.extensions.getPercent import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.ThumbnailsCacheManager import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.db.OCUpload import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.network.OnDatatransferProgressListener import com.owncloud.android.lib.common.operations.RemoteOperationResult @@ -35,6 +37,7 @@ import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.utils.ErrorMessageAdapter import com.owncloud.android.utils.theme.ViewThemeUtils import java.io.File +import kotlin.random.Random @Suppress("LongParameterList") class FileUploadWorker( @@ -48,20 +51,28 @@ class FileUploadWorker( val preferences: AppPreferences, val context: Context, params: WorkerParameters -) : Worker(context, params), OnDatatransferProgressListener { +) : Worker(context, params), + OnDatatransferProgressListener { companion object { val TAG: String = FileUploadWorker::class.java.simpleName const val NOTIFICATION_ERROR_ID: Int = 413 - private const val MAX_PROGRESS: Int = 100 + const val ACCOUNT = "data_account" + const val UPLOAD_IDS = "uploads_ids" + const val CURRENT_BATCH_INDEX = "batch_index" + const val TOTAL_UPLOAD_SIZE = "total_upload_size" + const val SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION = "show_same_file_already_exists_notification" + var currentUploadFileOperation: UploadFileOperation? = null private const val UPLOADS_ADDED_MESSAGE = "UPLOADS_ADDED" private const val UPLOAD_START_MESSAGE = "UPLOAD_START" private const val UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH" + private const val BATCH_SIZE = 100 + const val EXTRA_UPLOAD_RESULT = "RESULT" const val EXTRA_REMOTE_PATH = "REMOTE_PATH" const val EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH" @@ -75,35 +86,32 @@ class FileUploadWorker( const val LOCAL_BEHAVIOUR_FORGET = 2 const val LOCAL_BEHAVIOUR_DELETE = 3 - fun getUploadsAddedMessage(): String { - return FileUploadWorker::class.java.name + UPLOADS_ADDED_MESSAGE - } + fun getUploadsAddedMessage(): String = FileUploadWorker::class.java.name + UPLOADS_ADDED_MESSAGE - fun getUploadStartMessage(): String { - return FileUploadWorker::class.java.name + UPLOAD_START_MESSAGE - } + fun getUploadStartMessage(): String = FileUploadWorker::class.java.name + UPLOAD_START_MESSAGE - fun getUploadFinishMessage(): String { - return FileUploadWorker::class.java.name + UPLOAD_FINISH_MESSAGE - } + fun getUploadFinishMessage(): String = FileUploadWorker::class.java.name + UPLOAD_FINISH_MESSAGE } private var lastPercent = 0 - private val notificationManager = UploadNotificationManager(context, viewThemeUtils) + private val notificationManager = UploadNotificationManager(context, viewThemeUtils, Random.nextInt()) private val intents = FileUploaderIntents(context) private val fileUploaderDelegate = FileUploaderDelegate() @Suppress("TooGenericExceptionCaught") - override fun doWork(): Result { - return try { - backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) - val result = retrievePagesBySortingUploadsByID() - backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) - result - } catch (t: Throwable) { - Log_OC.e(TAG, "Error caught at FileUploadWorker " + t.localizedMessage) - Result.failure() + override fun doWork(): Result = try { + Log_OC.d(TAG, "FileUploadWorker started") + backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) + val result = uploadFiles() + backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) + notificationManager.dismissNotification() + if (result == Result.success()) { + setIdleWorkerState() } + result + } catch (t: Throwable) { + Log_OC.e(TAG, "Error caught at FileUploadWorker $t") + Result.failure() } override fun onStopped() { @@ -111,27 +119,59 @@ class FileUploadWorker( setIdleWorkerState() currentUploadFileOperation?.cancel(null) - notificationManager.dismissWorkerNotifications() + notificationManager.dismissNotification() super.onStopped() } - private fun setWorkerState(user: User?, uploads: List) { - WorkerStateLiveData.instance().setWorkState(WorkerState.Upload(user, uploads)) + private fun setWorkerState(user: User?) { + WorkerStateLiveData.instance().setWorkState(WorkerState.UploadStarted(user)) } private fun setIdleWorkerState() { - WorkerStateLiveData.instance().setWorkState(WorkerState.Idle) + WorkerStateLiveData.instance().setWorkState(WorkerState.UploadFinished(currentUploadFileOperation?.file)) } - @Suppress("ReturnCount") - private fun retrievePagesBySortingUploadsByID(): Result { - val accountName = inputData.getString(ACCOUNT) ?: return Result.failure() - var currentPage = uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(-1, accountName) + @Suppress("ReturnCount", "LongMethod") + private fun uploadFiles(): Result { + val accountName = inputData.getString(ACCOUNT) + if (accountName == null) { + Log_OC.e(TAG, "accountName is null") + return Result.failure() + } - notificationManager.dismissWorkerNotifications() + val uploadIds = inputData.getLongArray(UPLOAD_IDS) + if (uploadIds == null) { + Log_OC.e(TAG, "uploadIds is null") + return Result.failure() + } - while (currentPage.isNotEmpty() && !isStopped) { + val currentBatchIndex = inputData.getInt(CURRENT_BATCH_INDEX, -1) + if (currentBatchIndex == -1) { + Log_OC.e(TAG, "currentBatchIndex is -1, cancelling") + return Result.failure() + } + + val totalUploadSize = inputData.getInt(TOTAL_UPLOAD_SIZE, -1) + if (totalUploadSize == -1) { + Log_OC.e(TAG, "totalUploadSize is -1, cancelling") + return Result.failure() + } + + // since worker's policy is append or replace and account name comes from there no need check in the loop + val optionalUser = userAccountManager.getUser(accountName) + if (!optionalUser.isPresent) { + Log_OC.e(TAG, "User not found for account: $accountName") + return Result.failure() + } + + val user = optionalUser.get() + val previouslyUploadedFileSize = currentBatchIndex * FileUploadHelper.MAX_FILE_COUNT + val uploads = uploadsStorageManager.getUploadsByIds(uploadIds, accountName) + val ocAccount = OwnCloudAccount(user.toPlatformAccount(), context) + val client = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context) + + for ((index, upload) in uploads.withIndex()) { if (preferences.isGlobalUploadPaused) { Log_OC.d(TAG, "Upload is paused, skip uploading files!") notificationManager.notifyPaused( @@ -140,86 +180,101 @@ class FileUploadWorker( return Result.success() } - Log_OC.d(TAG, "Handling ${currentPage.size} uploads for account $accountName") - val lastId = currentPage.last().uploadId - uploadFiles(currentPage, accountName) - currentPage = - uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(lastId, accountName) + if (canExitEarly()) { + notificationManager.showConnectionErrorNotification() + return Result.failure() + } + + if (isStopped) { + continue + } + + setWorkerState(user) + val operation = createUploadFileOperation(upload, user) + currentUploadFileOperation = operation + + val currentIndex = (index + 1) + val currentUploadIndex = (currentIndex + previouslyUploadedFileSize) + notificationManager.prepareForStart( + operation, + cancelPendingIntent = intents.startIntent(operation), + startIntent = intents.notificationStartIntent(operation), + currentUploadIndex = currentUploadIndex, + totalUploadSize = totalUploadSize + ) + + val result = upload(operation, user, client) + currentUploadFileOperation = null + sendUploadFinishEvent(totalUploadSize, currentUploadIndex, operation, result) } - if (isStopped) { - Log_OC.d(TAG, "FileUploadWorker for account $accountName was stopped") - } else { - Log_OC.d(TAG, "No more pending uploads for account $accountName, stopping work") - } return Result.success() } - private fun uploadFiles(uploads: List, accountName: String) { - val user = userAccountManager.getUser(accountName) - setWorkerState(user.get(), uploads) + private fun sendUploadFinishEvent( + totalUploadSize: Int, + currentUploadIndex: Int, + operation: UploadFileOperation, + result: RemoteOperationResult<*> + ) { + val shouldBroadcast = + (totalUploadSize > BATCH_SIZE && currentUploadIndex > 0) && currentUploadIndex % BATCH_SIZE == 0 - for (upload in uploads) { - if (isStopped) { - break - } - - if (user.isPresent) { - val uploadFileOperation = createUploadFileOperation(upload, user.get()) - - currentUploadFileOperation = uploadFileOperation - val result = upload(uploadFileOperation, user.get()) - currentUploadFileOperation = null - - fileUploaderDelegate.sendBroadcastUploadFinished( - uploadFileOperation, - result, - uploadFileOperation.oldFile?.storagePath, - context, - localBroadcastManager - ) - } else { - uploadsStorageManager.removeUpload(upload.uploadId) - } + if (shouldBroadcast) { + // delay broadcast + fileUploaderDelegate.sendBroadcastUploadFinished( + operation, + result, + operation.oldFile?.storagePath, + context, + localBroadcastManager + ) } } - private fun createUploadFileOperation(upload: OCUpload, user: User): UploadFileOperation { - return UploadFileOperation( - uploadsStorageManager, - connectivityService, - powerManagementService, - user, - null, - upload, - upload.nameCollisionPolicy, - upload.localAction, - context, - upload.isUseWifiOnly, - upload.isWhileChargingOnly, - true, - FileDataStorageManager(user, context.contentResolver) - ).apply { - addDataTransferProgressListener(this@FileUploadWorker) + private fun canExitEarly(): Boolean { + val result = !connectivityService.isConnected || + connectivityService.isInternetWalled || + isStopped + + if (result) { + Log_OC.d(TAG, "No internet connection, stopping worker.") + } else { + notificationManager.dismissErrorNotification() } + + return result + } + + private fun createUploadFileOperation(upload: OCUpload, user: User): UploadFileOperation = UploadFileOperation( + uploadsStorageManager, + connectivityService, + powerManagementService, + user, + null, + upload, + upload.nameCollisionPolicy, + upload.localAction, + context, + upload.isUseWifiOnly, + upload.isWhileChargingOnly, + true, + FileDataStorageManager(user, context.contentResolver) + ).apply { + addDataTransferProgressListener(this@FileUploadWorker) } @Suppress("TooGenericExceptionCaught", "DEPRECATION") - private fun upload(uploadFileOperation: UploadFileOperation, user: User): RemoteOperationResult { + private fun upload( + uploadFileOperation: UploadFileOperation, + user: User, + client: OwnCloudClient + ): RemoteOperationResult { lateinit var result: RemoteOperationResult - notificationManager.prepareForStart( - uploadFileOperation, - cancelPendingIntent = intents.startIntent(uploadFileOperation), - intents.notificationStartIntent(uploadFileOperation) - ) - try { val storageManager = uploadFileOperation.storageManager - val ocAccount = OwnCloudAccount(user.toPlatformAccount(), context) - val uploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context) - result = uploadFileOperation.execute(uploadClient) - + result = uploadFileOperation.execute(client) val task = ThumbnailsCacheManager.ThumbnailGenerationTask(storageManager, user) val file = File(uploadFileOperation.originalStoragePath) val remoteId: String? = uploadFileOperation.file.remoteId @@ -238,16 +293,17 @@ class FileUploadWorker( if (!isStopped || !result.isCancelled) { uploadsStorageManager.updateDatabaseUploadResult(result, uploadFileOperation) notifyUploadResult(uploadFileOperation, result) - notificationManager.dismissWorkerNotifications() } } - @Suppress("ReturnCount") + @Suppress("ReturnCount", "LongMethod") private fun notifyUploadResult( uploadFileOperation: UploadFileOperation, uploadResult: RemoteOperationResult ) { Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.code) + val showSameFileAlreadyExistsNotification = + inputData.getBoolean(SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION, false) if (uploadResult.isSuccess) { notificationManager.dismissOldErrorNotification(uploadFileOperation) @@ -259,10 +315,19 @@ class FileUploadWorker( } // Only notify if it is not same file on remote that causes conflict - if (uploadResult.code == ResultCode.SYNC_CONFLICT && FileUploadHelper().isSameFileOnRemote( - uploadFileOperation.user, File(uploadFileOperation.storagePath), uploadFileOperation.remotePath, context + if (uploadResult.code == ResultCode.SYNC_CONFLICT && + FileUploadHelper().isSameFileOnRemote( + uploadFileOperation.user, + File(uploadFileOperation.storagePath), + uploadFileOperation.remotePath, + context ) ) { + if (showSameFileAlreadyExistsNotification) { + notificationManager.showSameFileAlreadyExistsNotification(uploadFileOperation.fileName) + } + + uploadFileOperation.handleLocalBehaviour() return } @@ -300,31 +365,51 @@ class FileUploadWorker( null } - notifyForFailedResult(uploadResult.code, conflictResolveIntent, credentialIntent, errorMessage) - showNewNotification(uploadFileOperation) + val cancelUploadActionIntent = if (conflictResolveIntent != null) { + intents.cancelUploadActionIntent(uploadFileOperation) + } else { + null + } + + notifyForFailedResult( + uploadFileOperation, + uploadResult.code, + conflictResolveIntent, + cancelUploadActionIntent, + credentialIntent, + errorMessage + ) } } + @Suppress("MagicNumber") + private val minProgressUpdateInterval = 750 + private var lastUpdateTime = 0L + + /** + * Receives from [com.owncloud.android.operations.UploadFileOperation.normalUpload] + */ + @Suppress("MagicNumber") override fun onTransferProgress( progressRate: Long, totalTransferredSoFar: Long, totalToTransfer: Long, fileAbsoluteName: String ) { - val percent = (MAX_PROGRESS * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt() + val percent = getPercent(totalTransferredSoFar, totalToTransfer) + val currentTime = System.currentTimeMillis() - if (percent != lastPercent) { + if (percent != lastPercent && (currentTime - lastUpdateTime) >= minProgressUpdateInterval) { notificationManager.run { val accountName = currentUploadFileOperation?.user?.accountName val remotePath = currentUploadFileOperation?.remotePath - val filename = currentUploadFileOperation?.fileName ?: "" - updateUploadProgress(filename, percent, currentUploadFileOperation) + updateUploadProgress(percent, currentUploadFileOperation) if (accountName != null && remotePath != null) { - val key: String = - FileUploadHelper.buildRemoteName(accountName, remotePath) + val key: String = FileUploadHelper.buildRemoteName(accountName, remotePath) val boundListener = FileUploadHelper.mBoundListeners[key] + val filename = currentUploadFileOperation?.fileName ?: "" boundListener?.onTransferProgress( progressRate, @@ -336,6 +421,7 @@ class FileUploadWorker( dismissOldErrorNotification(currentUploadFileOperation) } + lastUpdateTime = currentTime } lastPercent = percent diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt index d9d3726..2cff0b4 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt index 13fd8b8..b99310f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload @@ -12,7 +12,6 @@ import android.content.Context import android.content.Intent import android.os.Build import com.owncloud.android.authentication.AuthenticatorActivity -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.ConflictsResolveActivity.Companion.createIntent import com.owncloud.android.ui.activity.UploadListActivity @@ -57,32 +56,6 @@ class FileUploaderIntents(private val context: Context) { ) } - fun resultIntent(resultCode: ResultCode, operation: UploadFileOperation): PendingIntent { - val intent = if (resultCode == ResultCode.SYNC_CONFLICT) { - createIntent( - operation.file, - operation.user, - operation.ocUploadId, - Intent.FLAG_ACTIVITY_CLEAR_TOP, - context - ) - } else { - UploadListActivity.createIntent( - operation.file, - operation.user, - Intent.FLAG_ACTIVITY_CLEAR_TOP, - context - ) - } - - return PendingIntent.getActivity( - context, - System.currentTimeMillis().toInt(), - intent, - PendingIntent.FLAG_IMMUTABLE - ) - } - fun notificationStartIntent(operation: UploadFileOperation?): PendingIntent { val intent = UploadListActivity.createIntent( operation?.file, @@ -119,4 +92,19 @@ class FileUploaderIntents(private val context: Context) { ) } } + + fun cancelUploadActionIntent(uploadFileOperation: UploadFileOperation): PendingIntent { + val intent = Intent(context, FileUploadBroadcastReceiver::class.java).apply { + putExtra(FileUploadBroadcastReceiver.UPLOAD_ID, uploadFileOperation.ocUploadId) + putExtra(FileUploadBroadcastReceiver.REMOTE_PATH, uploadFileOperation.file.remotePath) + putExtra(FileUploadBroadcastReceiver.STORAGE_PATH, uploadFileOperation.file.storagePath) + } + + return PendingIntent.getBroadcast( + context, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/PostUploadAction.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/PostUploadAction.kt index 28e8eec..2bbf621 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/PostUploadAction.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/PostUploadAction.kt @@ -4,7 +4,7 @@ * @author Chris Narkiewicz * Copyright (C) 2021 Chris Narkiewicz * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt index 4c2a9c6..4ba76bf 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt @@ -1,65 +1,58 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload -import android.app.Notification -import android.app.NotificationManager import android.app.PendingIntent import android.content.Context -import android.graphics.BitmapFactory -import android.os.Build -import androidx.core.app.NotificationCompat +import com.nextcloud.client.jobs.notification.WorkerNotificationManager +import com.nextcloud.utils.extensions.isFileSpecificError +import com.nextcloud.utils.numberFormatter.NumberFormatter import com.owncloud.android.R import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.theme.ViewThemeUtils -class UploadNotificationManager(private val context: Context, viewThemeUtils: ViewThemeUtils) { - companion object { - private const val ID = 411 - } - - private var notification: Notification? = null - private var notificationBuilder: NotificationCompat.Builder = - NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { - setContentTitle(context.getString(R.string.foreground_service_upload)) - setSmallIcon(R.drawable.notification_icon) - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon)) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD) - } - } - private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - init { - notification = notificationBuilder.build() - } +class UploadNotificationManager(private val context: Context, viewThemeUtils: ViewThemeUtils, id: Int) : + WorkerNotificationManager( + id, + context, + viewThemeUtils, + tickerId = R.string.foreground_service_upload, + channelId = NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD + ) { @Suppress("MagicNumber") fun prepareForStart( uploadFileOperation: UploadFileOperation, cancelPendingIntent: PendingIntent, - startIntent: PendingIntent + startIntent: PendingIntent, + currentUploadIndex: Int, + totalUploadSize: Int ) { - notificationBuilder.run { - setContentTitle(context.getString(R.string.uploader_upload_in_progress_ticker)) - setContentText( - String.format( - context.getString(R.string.uploader_upload_in_progress), - 0, - uploadFileOperation.fileName - ) + currentOperationTitle = if (totalUploadSize > 1) { + String.format( + context.getString(R.string.upload_notification_manager_start_text), + currentUploadIndex, + totalUploadSize, + uploadFileOperation.fileName ) - setTicker(context.getString(R.string.foreground_service_upload)) + } else { + uploadFileOperation.fileName + } + + val progressText = NumberFormatter.getPercentageText(0) + + notificationBuilder.run { setProgress(100, 0, false) - setOngoing(true) + setContentTitle(currentOperationTitle) + setContentText(progressText) + setOngoing(false) clearActions() addAction( @@ -76,13 +69,27 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi } } + @Suppress("MagicNumber") + fun updateUploadProgress(percent: Int, currentOperation: UploadFileOperation?) { + val progressText = NumberFormatter.getPercentageText(percent) + setProgress(percent, progressText, false) + showNotification() + dismissOldErrorNotification(currentOperation) + } + fun notifyForFailedResult( + uploadFileOperation: UploadFileOperation, resultCode: RemoteOperationResult.ResultCode, conflictsResolveIntent: PendingIntent?, + cancelUploadActionIntent: PendingIntent?, credentialIntent: PendingIntent?, errorMessage: String ) { - val textId = resultTitle(resultCode) + if (uploadFileOperation.isMissingPermissionThrown) { + return + } + + val textId = getFailedResultTitleId(resultCode) notificationBuilder.run { setTicker(context.getString(textId)) @@ -100,15 +107,29 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi ) } + cancelUploadActionIntent?.let { + addAction( + R.drawable.ic_delete, + R.string.upload_list_cancel_upload, + cancelUploadActionIntent + ) + } + credentialIntent?.let { setContentIntent(it) } setContentText(errorMessage) } + + if (resultCode.isFileSpecificError()) { + showNewNotification(uploadFileOperation) + } else { + showNotification() + } } - private fun resultTitle(resultCode: RemoteOperationResult.ResultCode): Int { + private fun getFailedResultTitleId(resultCode: RemoteOperationResult.ResultCode): Int { val needsToUpdateCredentials = (resultCode == RemoteOperationResult.ResultCode.UNAUTHORIZED) return if (needsToUpdateCredentials) { @@ -128,7 +149,7 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi ) } - fun showNewNotification(operation: UploadFileOperation) { + private fun showNewNotification(operation: UploadFileOperation) { notificationManager.notify( NotificationUtils.createUploadNotificationTag(operation.file), FileUploadWorker.NOTIFICATION_ERROR_ID, @@ -136,20 +157,35 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi ) } - private fun showNotification() { - notificationManager.notify(ID, notificationBuilder.build()) + fun showSameFileAlreadyExistsNotification(filename: String) { + notificationBuilder.run { + setAutoCancel(true) + clearActions() + setContentText("") + setProgress(0, 0, false) + setContentTitle(context.getString(R.string.file_upload_worker_same_file_already_exists, filename)) + } + + val notificationId = filename.hashCode() + + notificationManager.notify( + notificationId, + notificationBuilder.build() + ) } - @Suppress("MagicNumber") - fun updateUploadProgress(filename: String, percent: Int, currentOperation: UploadFileOperation?) { - notificationBuilder.run { - setProgress(100, percent, false) - val text = String.format(context.getString(R.string.uploader_upload_in_progress), percent, filename) - setContentText(text) + fun showConnectionErrorNotification() { + notificationManager.cancel(getId()) - showNotification() - dismissOldErrorNotification(currentOperation) + notificationBuilder.run { + setContentTitle(context.getString(R.string.file_upload_worker_error_notification_title)) + setContentText("") } + + notificationManager.notify( + FileUploadWorker.NOTIFICATION_ERROR_ID, + notificationBuilder.build() + ) } fun dismissOldErrorNotification(operation: UploadFileOperation?) { @@ -164,6 +200,8 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi } } + fun dismissErrorNotification() = notificationManager.cancel(FileUploadWorker.NOTIFICATION_ERROR_ID) + fun dismissOldErrorNotification(remotePath: String, localPath: String) { notificationManager.cancel( NotificationUtils.createUploadNotificationTag(remotePath, localPath), @@ -171,15 +209,11 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi ) } - fun dismissWorkerNotifications() { - notificationManager.cancel(ID) - } - fun notifyPaused(intent: PendingIntent) { - notificationBuilder.apply { + notificationBuilder.run { setContentTitle(context.getString(R.string.upload_global_pause_title)) setTicker(context.getString(R.string.upload_global_pause_title)) - setOngoing(true) + setOngoing(false) setAutoCancel(false) setProgress(0, 0, false) clearActions() diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt index 08f4585..21aa361 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload @@ -44,16 +44,14 @@ class UploadTask( private val clientProvider: () -> OwnCloudClient, private val fileDataStorageManager: FileDataStorageManager ) { - fun create(): UploadTask { - return UploadTask( - applicationContext, - uploadsStorageManager, - connectivityService, - powerManagementService, - clientProvider, - fileDataStorageManager - ) - } + fun create(): UploadTask = UploadTask( + applicationContext, + uploadsStorageManager, + connectivityService, + powerManagementService, + clientProvider, + fileDataStorageManager + ) } fun upload(user: User, upload: OCUpload): Result { diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt index 3712cb4..a378f59 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload diff --git a/app/src/main/java/com/nextcloud/client/logger/FileLogHandler.kt b/app/src/main/java/com/nextcloud/client/logger/FileLogHandler.kt index ef01ca9..dc1409d 100644 --- a/app/src/main/java/com/nextcloud/client/logger/FileLogHandler.kt +++ b/app/src/main/java/com/nextcloud/client/logger/FileLogHandler.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger diff --git a/app/src/main/java/com/nextcloud/client/logger/LegacyLoggerAdapter.kt b/app/src/main/java/com/nextcloud/client/logger/LegacyLoggerAdapter.kt index 3c7805e..0d9f845 100644 --- a/app/src/main/java/com/nextcloud/client/logger/LegacyLoggerAdapter.kt +++ b/app/src/main/java/com/nextcloud/client/logger/LegacyLoggerAdapter.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger diff --git a/app/src/main/java/com/nextcloud/client/logger/Level.kt b/app/src/main/java/com/nextcloud/client/logger/Level.kt index 9aa2562..cac864e 100644 --- a/app/src/main/java/com/nextcloud/client/logger/Level.kt +++ b/app/src/main/java/com/nextcloud/client/logger/Level.kt @@ -2,10 +2,12 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger +import com.owncloud.android.R + enum class Level(val tag: String) { UNKNOWN("U"), VERBOSE("V"), @@ -15,6 +17,16 @@ enum class Level(val tag: String) { ERROR("E"), ASSERT("A"); + fun getColor(): Int = when (this) { + UNKNOWN -> R.color.log_level_unknown + VERBOSE -> R.color.log_level_verbose + DEBUG -> R.color.log_level_debug + INFO -> R.color.log_level_info + WARNING -> R.color.log_level_warning + ASSERT -> R.color.log_level_assert + ERROR -> R.color.log_level_error + } + companion object { @JvmStatic fun fromTag(tag: String): Level = when (tag) { diff --git a/app/src/main/java/com/nextcloud/client/logger/LogEntry.kt b/app/src/main/java/com/nextcloud/client/logger/LogEntry.kt index a0206da..6e47993 100644 --- a/app/src/main/java/com/nextcloud/client/logger/LogEntry.kt +++ b/app/src/main/java/com/nextcloud/client/logger/LogEntry.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger @@ -33,17 +33,15 @@ data class LogEntry(val timestamp: Date, val level: Level, val tag: String, val ) @JvmStatic - fun buildDateFormat(tz: TimeZone? = null): SimpleDateFormat { - return if (tz == null) { - SimpleDateFormat(UTC_DATE_FORMAT, Locale.US).apply { - timeZone = TIME_ZONE - isLenient = false - } - } else { - SimpleDateFormat(TZ_DATE_FORMAT, Locale.US).apply { - timeZone = tz - isLenient = false - } + fun buildDateFormat(tz: TimeZone? = null): SimpleDateFormat = if (tz == null) { + SimpleDateFormat(UTC_DATE_FORMAT, Locale.US).apply { + timeZone = TIME_ZONE + isLenient = false + } + } else { + SimpleDateFormat(TZ_DATE_FORMAT, Locale.US).apply { + timeZone = tz + isLenient = false } } diff --git a/app/src/main/java/com/nextcloud/client/logger/Logger.kt b/app/src/main/java/com/nextcloud/client/logger/Logger.kt index d7859ae..9a46105 100644 --- a/app/src/main/java/com/nextcloud/client/logger/Logger.kt +++ b/app/src/main/java/com/nextcloud/client/logger/Logger.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger diff --git a/app/src/main/java/com/nextcloud/client/logger/LoggerImpl.kt b/app/src/main/java/com/nextcloud/client/logger/LoggerImpl.kt index c09c47a..148000c 100644 --- a/app/src/main/java/com/nextcloud/client/logger/LoggerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/logger/LoggerImpl.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger @@ -22,7 +22,8 @@ internal class LoggerImpl( private val handler: FileLogHandler, private val mainThreadHandler: Handler, queueCapacity: Int -) : Logger, LogsRepository { +) : Logger, + LogsRepository { data class Load(val onResult: (List, Long) -> Unit) class Delete diff --git a/app/src/main/java/com/nextcloud/client/logger/LogsRepository.kt b/app/src/main/java/com/nextcloud/client/logger/LogsRepository.kt index b0871c7..30b4444 100644 --- a/app/src/main/java/com/nextcloud/client/logger/LogsRepository.kt +++ b/app/src/main/java/com/nextcloud/client/logger/LogsRepository.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger diff --git a/app/src/main/java/com/nextcloud/client/logger/ThreadLoop.kt b/app/src/main/java/com/nextcloud/client/logger/ThreadLoop.kt index 51b2cce..b56fe69 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ThreadLoop.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ThreadLoop.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/AsyncFilter.kt b/app/src/main/java/com/nextcloud/client/logger/ui/AsyncFilter.kt index 1c033ce..d42fc0f 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/AsyncFilter.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/AsyncFilter.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger.ui diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt index 2e9dd85..214750c 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger.ui @@ -20,7 +20,6 @@ import com.nextcloud.client.di.ViewModelFactory import com.owncloud.android.R import com.owncloud.android.databinding.LogsActivityBinding import com.owncloud.android.ui.activity.ToolbarActivity -import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject class LogsActivity : ToolbarActivity() { @@ -28,17 +27,12 @@ class LogsActivity : ToolbarActivity() { @Inject lateinit var viewModelFactory: ViewModelFactory - @Inject - lateinit var viewThemeUtils: ViewThemeUtils - private lateinit var vm: LogsViewModel private lateinit var binding: LogsActivityBinding private lateinit var logsAdapter: LogsAdapter private val searchBoxListener = object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String): Boolean { - return false - } + override fun onQueryTextSubmit(query: String): Boolean = false override fun onQueryTextChange(newText: String): Boolean { vm.filter(newText) diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsAdapter.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsAdapter.kt index 97141d5..35def37 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsAdapter.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsAdapter.kt @@ -2,34 +2,34 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger.ui +import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.nextcloud.client.logger.LogEntry import com.owncloud.android.R -import java.text.SimpleDateFormat -import java.util.Locale -class LogsAdapter(context: Context) : RecyclerView.Adapter() { +class LogsAdapter(private val context: Context) : RecyclerView.Adapter() { class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { - val header = view.findViewById(R.id.log_entry_list_item_header) - val message = view.findViewById(R.id.log_entry_list_item_message) + val header: TextView? = view.findViewById(R.id.log_entry_list_item_header) + val message: TextView? = view.findViewById(R.id.log_entry_list_item_message) } - private val timestampFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) private val inflater = LayoutInflater.from(context) var entries: List = listOf() + @SuppressLint("NotifyDataSetChanged") set(value) { - field = value + field = value.sortedBy { it.timestamp } notifyDataSetChanged() } @@ -39,10 +39,14 @@ class LogsAdapter(context: Context) : RecyclerView.Adapter - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger.ui @@ -86,12 +86,10 @@ class LogsEmailSender(private val context: Context, private val clock: Clock, pr } } - private fun getPhoneInfo(): String { - return "Model: " + Build.MODEL + "\n" + - "Brand: " + Build.BRAND + "\n" + - "Product: " + Build.PRODUCT + "\n" + - "Device: " + Build.DEVICE + "\n" + - "Version-Codename: " + Build.VERSION.CODENAME + "\n" + - "Version-Release: " + Build.VERSION.RELEASE - } + private fun getPhoneInfo(): String = "Model: " + Build.MODEL + "\n" + + "Brand: " + Build.BRAND + "\n" + + "Product: " + Build.PRODUCT + "\n" + + "Device: " + Build.DEVICE + "\n" + + "Version-Codename: " + Build.VERSION.CODENAME + "\n" + + "Version-Release: " + Build.VERSION.RELEASE } diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt index b81ac22..c773758 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.logger.ui diff --git a/app/src/main/java/com/nextcloud/client/media/AudioFocus.kt b/app/src/main/java/com/nextcloud/client/media/AudioFocus.kt index a151a75..c60702b 100644 --- a/app/src/main/java/com/nextcloud/client/media/AudioFocus.kt +++ b/app/src/main/java/com/nextcloud/client/media/AudioFocus.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media diff --git a/app/src/main/java/com/nextcloud/client/media/AudioFocusManager.kt b/app/src/main/java/com/nextcloud/client/media/AudioFocusManager.kt index 2885092..358b393 100644 --- a/app/src/main/java/com/nextcloud/client/media/AudioFocusManager.kt +++ b/app/src/main/java/com/nextcloud/client/media/AudioFocusManager.kt @@ -2,13 +2,12 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media import android.media.AudioFocusRequest import android.media.AudioManager -import android.os.Build /** * Wrapper around audio manager exposing simplified audio focus API and @@ -19,45 +18,29 @@ import android.os.Build */ internal class AudioFocusManager( private val audioManger: AudioManager, - private val onFocusChange: (AudioFocus) -> Unit + private val onFocusChange: (AudioFocus) -> Unit, + requestBuilder: AudioFocusRequest.Builder = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) ) { - - private val focusListener = object : AudioManager.OnAudioFocusChangeListener { - override fun onAudioFocusChange(focusChange: Int) { - val focus = when (focusChange) { - AudioManager.AUDIOFOCUS_GAIN -> AudioFocus.FOCUS - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT -> AudioFocus.FOCUS - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK -> AudioFocus.FOCUS - AudioManager.AUDIOFOCUS_LOSS -> AudioFocus.LOST - AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> AudioFocus.LOST - AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> AudioFocus.DUCK - else -> null - } - focus?.let { onFocusChange(it) } - } - } - private var focusRequest: AudioFocusRequest? = null - - init { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run { - setWillPauseWhenDucked(true) - setOnAudioFocusChangeListener(focusListener) - }.build() + private val focusListener = AudioManager.OnAudioFocusChangeListener { focusChange -> + val focus = when (focusChange) { + AudioManager.AUDIOFOCUS_GAIN, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK -> AudioFocus.FOCUS + AudioManager.AUDIOFOCUS_LOSS, + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> AudioFocus.LOST + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> AudioFocus.DUCK + else -> null } + focus?.let { onFocusChange(it) } } - /** - * Request audio focus. Focus is reported via callback. - * If focus cannot be gained, lost of focus is reported. - */ + private val focusRequest = requestBuilder + .setWillPauseWhenDucked(true) + .setOnAudioFocusChangeListener(focusListener) + .build() + fun requestFocus() { - val requestResult = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - focusRequest?.let { audioManger.requestAudioFocus(it) } - } else { - audioManger.requestAudioFocus(focusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) - } - + val requestResult = audioManger.requestAudioFocus(focusRequest) if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { focusListener.onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN) } else { @@ -65,17 +48,8 @@ internal class AudioFocusManager( } } - /** - * Release audio focus. Loss of focus is reported via callback. - */ fun releaseFocus() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - focusRequest?.let { - audioManger.abandonAudioFocusRequest(it) - } ?: AudioManager.AUDIOFOCUS_REQUEST_FAILED - } else { - audioManger.abandonAudioFocus(focusListener) - } + audioManger.abandonAudioFocusRequest(focusRequest) focusListener.onAudioFocusChange(AudioManager.AUDIOFOCUS_LOSS) } } diff --git a/app/src/main/java/com/nextcloud/client/media/BackgroundPlayerService.kt b/app/src/main/java/com/nextcloud/client/media/BackgroundPlayerService.kt new file mode 100644 index 0000000..7e1adca --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/media/BackgroundPlayerService.kt @@ -0,0 +1,241 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Parneet Singh + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.media + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import androidx.annotation.OptIn +import androidx.media3.common.Player +import androidx.media3.common.Player.COMMAND_PLAY_PAUSE +import androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT +import androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM +import androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS +import androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.session.CommandButton +import androidx.media3.session.DefaultMediaNotificationProvider +import androidx.media3.session.DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX +import androidx.media3.session.MediaSession +import androidx.media3.session.MediaSession.ConnectionResult +import androidx.media3.session.MediaSession.ConnectionResult.AcceptedResultBuilder +import androidx.media3.session.MediaSessionService +import androidx.media3.session.SessionCommand +import androidx.media3.session.SessionResult +import com.google.common.collect.ImmutableList +import com.google.common.util.concurrent.Futures +import com.google.common.util.concurrent.ListenableFuture +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.media.NextcloudExoPlayer.createNextcloudExoplayer +import com.nextcloud.client.network.ClientFactory +import com.nextcloud.common.NextcloudClient +import com.nextcloud.utils.extensions.registerBroadcastReceiver +import com.owncloud.android.MainApp +import com.owncloud.android.datamodel.ReceiverFlag +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import javax.inject.Inject + +@OptIn(UnstableApi::class) +class BackgroundPlayerService : + MediaSessionService(), + Injectable { + + private val seekBackSessionCommand = SessionCommand(SESSION_COMMAND_ACTION_SEEK_BACK, Bundle.EMPTY) + private val seekForwardSessionCommand = SessionCommand(SESSION_COMMAND_ACTION_SEEK_FORWARD, Bundle.EMPTY) + + val seekForward = + CommandButton.Builder() + .setDisplayName("Seek Forward") + .setIconResId(CommandButton.getIconResIdForIconConstant(CommandButton.ICON_SKIP_FORWARD_15)) + .setSessionCommand(seekForwardSessionCommand) + .setExtras(Bundle().apply { putInt(COMMAND_KEY_COMPACT_VIEW_INDEX, 2) }) + .build() + + val seekBackward = + CommandButton.Builder() + .setDisplayName("Seek Backward") + .setIconResId(CommandButton.getIconResIdForIconConstant(CommandButton.ICON_SKIP_BACK_5)) + .setSessionCommand(seekBackSessionCommand) + .setExtras(Bundle().apply { putInt(COMMAND_KEY_COMPACT_VIEW_INDEX, 0) }) + .build() + + @Inject + lateinit var clientFactory: ClientFactory + + @Inject + lateinit var userAccountManager: UserAccountManager + lateinit var exoPlayer: ExoPlayer + private var mediaSession: MediaSession? = null + + private val stopReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.action) { + RELEASE_MEDIA_SESSION_BROADCAST_ACTION -> release() + STOP_MEDIA_SESSION_BROADCAST_ACTION -> exoPlayer.stop() + } + } + } + + override fun onCreate() { + super.onCreate() + + registerBroadcastReceiver( + stopReceiver, + IntentFilter().apply { + addAction(RELEASE_MEDIA_SESSION_BROADCAST_ACTION) + addAction(STOP_MEDIA_SESSION_BROADCAST_ACTION) + }, + ReceiverFlag.NotExported + ) + + MainApp.getAppComponent().inject(this) + initNextcloudExoPlayer() + + setMediaNotificationProvider(object : DefaultMediaNotificationProvider(this) { + override fun getMediaButtons( + session: MediaSession, + playerCommands: Player.Commands, + customLayout: ImmutableList, + showPauseButton: Boolean + ): ImmutableList { + val playPauseButton = + CommandButton.Builder() + .setDisplayName("PlayPause") + .setIconResId( + CommandButton.getIconResIdForIconConstant( + if (mediaSession?.player?.isPlaying == true) { + CommandButton.ICON_PAUSE + } else { + CommandButton.ICON_PLAY + } + ) + ) + .setPlayerCommand(COMMAND_PLAY_PAUSE) + .setExtras(Bundle().apply { putInt(COMMAND_KEY_COMPACT_VIEW_INDEX, 1) }) + .build() + + val myCustomButtonsLayout = + ImmutableList.of(seekBackward, playPauseButton, seekForward) + return myCustomButtonsLayout + } + }) + } + + private fun initNextcloudExoPlayer() { + runBlocking { + var nextcloudClient: NextcloudClient + withContext(Dispatchers.IO) { + nextcloudClient = clientFactory.createNextcloudClient(userAccountManager.user) + } + nextcloudClient.let { + exoPlayer = createNextcloudExoplayer(this@BackgroundPlayerService, nextcloudClient) + mediaSession = + MediaSession.Builder(applicationContext, exoPlayer) + // set id to distinct this session to avoid crash + // in case session release delayed a bit and + // we start another session for eg. video + .setId(BACKGROUND_MEDIA_SESSION_ID) + .setCustomLayout(listOf(seekBackward, seekForward)) + .setCallback(object : MediaSession.Callback { + override fun onConnect( + session: MediaSession, + controller: MediaSession.ControllerInfo + ): ConnectionResult = AcceptedResultBuilder(mediaSession!!) + .setAvailablePlayerCommands( + ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() + .remove(COMMAND_SEEK_TO_NEXT) + .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) + .remove(COMMAND_SEEK_TO_PREVIOUS) + .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) + .build() + ) + .setAvailableSessionCommands( + ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() + .addSessionCommands( + listOf(seekBackSessionCommand, seekForwardSessionCommand) + ).build() + ) + .build() + + override fun onPostConnect(session: MediaSession, controller: MediaSession.ControllerInfo) { + session.setCustomLayout(listOf(seekBackward, seekForward)) + } + + override fun onCustomCommand( + session: MediaSession, + controller: MediaSession.ControllerInfo, + customCommand: SessionCommand, + args: Bundle + ): ListenableFuture = when (customCommand.customAction) { + SESSION_COMMAND_ACTION_SEEK_FORWARD -> { + session.player.seekForward() + Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) + } + + SESSION_COMMAND_ACTION_SEEK_BACK -> { + session.player.seekBack() + Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) + } + + else -> super.onCustomCommand(session, controller, customCommand, args) + } + }) + .build() + } + } + } + + override fun onTaskRemoved(rootIntent: Intent?) { + release() + } + + override fun onDestroy() { + unregisterReceiver(stopReceiver) + mediaSession?.run { + player.release() + release() + mediaSession = null + } + super.onDestroy() + } + + private fun release() { + val player = mediaSession?.player + if (player?.playWhenReady == true) { + // Make sure the service is not in foreground. + player.pause() + } + // Bug in Android 14, https://github.com/androidx/media/issues/805 + // that sometimes onTaskRemove() doesn't get called immediately + // eventually gets called so the service stops but the notification doesn't clear out. + // [WORKAROUND] So, explicitly removing the notification here. + // TODO revisit after bug solved! + val nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + nm.cancel(DefaultMediaNotificationProvider.DEFAULT_NOTIFICATION_ID) + stopSelf() + } + + override fun onGetSession(p0: MediaSession.ControllerInfo): MediaSession? = mediaSession + + companion object { + private const val SESSION_COMMAND_ACTION_SEEK_BACK = "SESSION_COMMAND_ACTION_SEEK_BACK" + private const val SESSION_COMMAND_ACTION_SEEK_FORWARD = "SESSION_COMMAND_ACTION_SEEK_FORWARD" + + private const val BACKGROUND_MEDIA_SESSION_ID = "com.nextcloud.client.media.BACKGROUND_MEDIA_SESSION_ID" + + const val RELEASE_MEDIA_SESSION_BROADCAST_ACTION = "com.nextcloud.client.media.RELEASE_MEDIA_SESSION" + const val STOP_MEDIA_SESSION_BROADCAST_ACTION = "com.nextcloud.client.media.STOP_MEDIA_SESSION" + } +} diff --git a/app/src/main/java/com/nextcloud/client/media/ErrorFormat.kt b/app/src/main/java/com/nextcloud/client/media/ErrorFormat.kt index 094a9b3..cb3cfec 100644 --- a/app/src/main/java/com/nextcloud/client/media/ErrorFormat.kt +++ b/app/src/main/java/com/nextcloud/client/media/ErrorFormat.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media diff --git a/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt b/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt index 40de9de..3b0dd56 100644 --- a/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt +++ b/app/src/main/java/com/nextcloud/client/media/ExoplayerListener.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media @@ -22,8 +22,7 @@ class ExoplayerListener( private val playerView: View, private val exoPlayer: ExoPlayer, private val onCompleted: () -> Unit = { } -) : - Player.Listener { +) : Player.Listener { override fun onPlaybackStateChanged(playbackState: Int) { super.onPlaybackStateChanged(playbackState) diff --git a/app/src/main/java/com/nextcloud/client/media/LoadUrlTask.kt b/app/src/main/java/com/nextcloud/client/media/LoadUrlTask.kt index a5b7e8c..ea69556 100644 --- a/app/src/main/java/com/nextcloud/client/media/LoadUrlTask.kt +++ b/app/src/main/java/com/nextcloud/client/media/LoadUrlTask.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2018 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media diff --git a/app/src/main/java/com/nextcloud/client/media/NextcloudExoPlayer.kt b/app/src/main/java/com/nextcloud/client/media/NextcloudExoPlayer.kt index 02b86ec..283f4e5 100644 --- a/app/src/main/java/com/nextcloud/client/media/NextcloudExoPlayer.kt +++ b/app/src/main/java/com/nextcloud/client/media/NextcloudExoPlayer.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media diff --git a/app/src/main/java/com/nextcloud/client/media/Player.kt b/app/src/main/java/com/nextcloud/client/media/Player.kt index 1ba6918..0074690 100644 --- a/app/src/main/java/com/nextcloud/client/media/Player.kt +++ b/app/src/main/java/com/nextcloud/client/media/Player.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media @@ -230,17 +230,11 @@ internal class Player( // region Media player controls - override fun isPlaying(): Boolean { - return stateMachine.isInState(State.PLAYING) - } + override fun isPlaying(): Boolean = stateMachine.isInState(State.PLAYING) - override fun canSeekForward(): Boolean { - return duration > MIN_DURATION_ALLOWING_SEEK - } + override fun canSeekForward(): Boolean = duration > MIN_DURATION_ALLOWING_SEEK - override fun canSeekBackward(): Boolean { - return duration > MIN_DURATION_ALLOWING_SEEK - } + override fun canSeekBackward(): Boolean = duration > MIN_DURATION_ALLOWING_SEEK override fun getDuration(): Int { val hasDuration = setOf(State.PLAYING, State.PAUSED) @@ -256,9 +250,7 @@ internal class Player( stateMachine.post(Event.PAUSE) } - override fun getBufferPercentage(): Int { - return 0 - } + override fun getBufferPercentage(): Int = 0 override fun seekTo(pos: Int) { if (stateMachine.isInState(State.PLAYING)) { @@ -266,21 +258,15 @@ internal class Player( } } - override fun getCurrentPosition(): Int { - return mediaPlayer?.currentPosition ?: 0 - } + override fun getCurrentPosition(): Int = mediaPlayer?.currentPosition ?: 0 override fun start() { stateMachine.post(Event.PLAY) } - override fun getAudioSessionId(): Int { - return 0 - } + override fun getAudioSessionId(): Int = 0 - override fun canPause(): Boolean { - return stateMachine.isInState(State.PLAYING) - } + override fun canPause(): Boolean = stateMachine.isInState(State.PLAYING) // endregion } diff --git a/app/src/main/java/com/nextcloud/client/media/PlayerError.kt b/app/src/main/java/com/nextcloud/client/media/PlayerError.kt index 78fc130..85a3808 100644 --- a/app/src/main/java/com/nextcloud/client/media/PlayerError.kt +++ b/app/src/main/java/com/nextcloud/client/media/PlayerError.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media diff --git a/app/src/main/java/com/nextcloud/client/media/PlayerService.kt b/app/src/main/java/com/nextcloud/client/media/PlayerService.kt index 6e4be16..e7c67a7 100644 --- a/app/src/main/java/com/nextcloud/client/media/PlayerService.kt +++ b/app/src/main/java/com/nextcloud/client/media/PlayerService.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media @@ -10,12 +10,12 @@ import android.app.PendingIntent import android.app.Service import android.content.Intent import android.media.AudioManager -import android.os.Build import android.os.Bundle import android.os.IBinder import android.widget.MediaController import android.widget.Toast import androidx.core.app.NotificationCompat +import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.nextcloud.client.account.User import com.nextcloud.client.network.ClientFactory import com.nextcloud.utils.ForegroundServiceHelper @@ -23,7 +23,9 @@ import com.nextcloud.utils.extensions.getParcelableArgument import com.owncloud.android.R import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.notifications.NotificationUtils +import com.owncloud.android.ui.preview.PreviewMediaActivity import com.owncloud.android.utils.theme.ViewThemeUtils import dagger.android.AndroidInjection import java.util.Locale @@ -32,6 +34,8 @@ import javax.inject.Inject class PlayerService : Service() { companion object { + private const val TAG = "PlayerService" + const val EXTRA_USER = "USER" const val EXTRA_FILE = "FILE" const val EXTRA_AUTO_PLAY = "EXTRA_AUTO_PLAY" @@ -40,6 +44,8 @@ class PlayerService : Service() { const val ACTION_STOP = "STOP" const val ACTION_TOGGLE = "TOGGLE" const val ACTION_STOP_FILE = "STOP_FILE" + + const val IS_MEDIA_CONTROL_LAYOUT_READY = "IS_MEDIA_CONTROL_LAYOUT_READY" } class Binder(val service: PlayerService) : android.os.Binder() { @@ -52,24 +58,34 @@ class PlayerService : Service() { } private val playerListener = object : Player.Listener { - override fun onRunning(file: OCFile) { + Log_OC.d(TAG, "PlayerService.onRunning()") + val intent = Intent(PreviewMediaActivity.MEDIA_CONTROL_READY_RECEIVER).apply { + putExtra(IS_MEDIA_CONTROL_LAYOUT_READY, false) + } + LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent) startForeground(file) } override fun onStart() { - // empty + Log_OC.d(TAG, "PlayerService.onStart()") + val intent = Intent(PreviewMediaActivity.MEDIA_CONTROL_READY_RECEIVER).apply { + putExtra(IS_MEDIA_CONTROL_LAYOUT_READY, true) + } + LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent) } override fun onPause() { - // empty + Log_OC.d(TAG, "PlayerService.onPause()") } override fun onStop() { + Log_OC.d(TAG, "PlayerService.onStop()") stopServiceAndRemoveNotification(null) } override fun onError(error: PlayerError) { + Log_OC.d(TAG, "PlayerService.onError()") Toast.makeText(this@PlayerService, error.message, Toast.LENGTH_SHORT).show() } } @@ -89,29 +105,32 @@ class PlayerService : Service() { override fun onCreate() { super.onCreate() + AndroidInjection.inject(this) player = Player(applicationContext, clientFactory, playerListener, audioManager) notificationBuilder = NotificationCompat.Builder(this) viewThemeUtils.androidx.themeNotificationCompatBuilder(this, notificationBuilder) - val stop = Intent(this, PlayerService::class.java) - stop.action = ACTION_STOP - val pendingStop = PendingIntent.getService(this, 0, stop, PendingIntent.FLAG_IMMUTABLE) - notificationBuilder.addAction(0, getString(R.string.player_stop).toUpperCase(Locale.getDefault()), pendingStop) + val stop = Intent(this, PlayerService::class.java).apply { + action = ACTION_STOP + } + + val pendingStop = PendingIntent.getService(this, 0, stop, PendingIntent.FLAG_IMMUTABLE) + notificationBuilder.addAction(0, getString(R.string.player_stop).lowercase(Locale.getDefault()), pendingStop) + + val toggle = Intent(this, PlayerService::class.java).apply { + action = ACTION_TOGGLE + } - val toggle = Intent(this, PlayerService::class.java) - toggle.action = ACTION_TOGGLE val pendingToggle = PendingIntent.getService(this, 0, toggle, PendingIntent.FLAG_IMMUTABLE) notificationBuilder.addAction( 0, - getString(R.string.player_toggle).toUpperCase(Locale.getDefault()), + getString(R.string.player_toggle).lowercase(Locale.getDefault()), pendingToggle ) } - override fun onBind(intent: Intent?): IBinder? { - return Binder(this) - } + override fun onBind(intent: Intent?): IBinder? = Binder(this) override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { when (intent.action) { @@ -124,10 +143,12 @@ class PlayerService : Service() { } private fun onActionToggle() { - if (player.isPlaying) { - player.pause() - } else { - player.start() + player.run { + if (isPlaying) { + pause() + } else { + start() + } } } @@ -153,14 +174,14 @@ class PlayerService : Service() { private fun startForeground(currentFile: OCFile) { val ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name)) val content = getString(R.string.media_state_playing, currentFile.getFileName()) - notificationBuilder.setSmallIcon(R.drawable.ic_play_arrow) - notificationBuilder.setWhen(System.currentTimeMillis()) - notificationBuilder.setOngoing(true) - notificationBuilder.setContentTitle(ticker) - notificationBuilder.setContentText(content) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_MEDIA) + notificationBuilder.run { + setSmallIcon(R.drawable.ic_play_arrow) + setWhen(System.currentTimeMillis()) + setOngoing(true) + setContentTitle(ticker) + setContentText(content) + setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_MEDIA) } ForegroundServiceHelper.startService( diff --git a/app/src/main/java/com/nextcloud/client/media/PlayerServiceConnection.kt b/app/src/main/java/com/nextcloud/client/media/PlayerServiceConnection.kt index 3d39cd6..2c89ca5 100644 --- a/app/src/main/java/com/nextcloud/client/media/PlayerServiceConnection.kt +++ b/app/src/main/java/com/nextcloud/client/media/PlayerServiceConnection.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media @@ -10,9 +10,9 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection -import android.os.Build import android.os.IBinder import android.widget.MediaController +import androidx.core.content.ContextCompat import com.nextcloud.client.account.User import com.owncloud.android.datamodel.OCFile @@ -38,12 +38,14 @@ class PlayerServiceConnection(private val context: Context) : MediaController.Me } fun start(user: User, file: OCFile, playImmediately: Boolean, position: Long) { - val i = Intent(context, PlayerService::class.java) - i.putExtra(PlayerService.EXTRA_USER, user) - i.putExtra(PlayerService.EXTRA_FILE, file) - i.putExtra(PlayerService.EXTRA_AUTO_PLAY, playImmediately) - i.putExtra(PlayerService.EXTRA_START_POSITION_MS, position) - i.action = PlayerService.ACTION_PLAY + val i = Intent(context, PlayerService::class.java).apply { + putExtra(PlayerService.EXTRA_USER, user) + putExtra(PlayerService.EXTRA_FILE, file) + putExtra(PlayerService.EXTRA_AUTO_PLAY, playImmediately) + putExtra(PlayerService.EXTRA_START_POSITION_MS, position) + action = PlayerService.ACTION_PLAY + } + startForegroundService(i) } @@ -84,57 +86,37 @@ class PlayerServiceConnection(private val context: Context) : MediaController.Me // region Media controller - override fun isPlaying(): Boolean { - return binder?.player?.isPlaying ?: false - } + override fun isPlaying(): Boolean = binder?.player?.isPlaying ?: false - override fun canSeekForward(): Boolean { - return binder?.player?.canSeekForward() ?: false - } + override fun canSeekForward(): Boolean = binder?.player?.canSeekForward() ?: false - override fun getDuration(): Int { - return binder?.player?.duration ?: 0 - } + override fun getDuration(): Int = binder?.player?.duration ?: 0 override fun pause() { binder?.player?.pause() } - override fun getBufferPercentage(): Int { - return binder?.player?.bufferPercentage ?: 0 - } + override fun getBufferPercentage(): Int = binder?.player?.bufferPercentage ?: 0 override fun seekTo(pos: Int) { binder?.player?.seekTo(pos) } - override fun getCurrentPosition(): Int { - return binder?.player?.currentPosition ?: 0 - } + override fun getCurrentPosition(): Int = binder?.player?.currentPosition ?: 0 - override fun canSeekBackward(): Boolean { - return binder?.player?.canSeekBackward() ?: false - } + override fun canSeekBackward(): Boolean = binder?.player?.canSeekBackward() ?: false override fun start() { binder?.player?.start() } - override fun getAudioSessionId(): Int { - return 0 - } + override fun getAudioSessionId(): Int = 0 - override fun canPause(): Boolean { - return binder?.player?.canPause() ?: false - } + override fun canPause(): Boolean = binder?.player?.canPause() ?: false // endregion private fun startForegroundService(i: Intent) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(i) - } else { - context.startService(i) - } + ContextCompat.startForegroundService(context, i) } } diff --git a/app/src/main/java/com/nextcloud/client/media/PlayerStateMachine.kt b/app/src/main/java/com/nextcloud/client/media/PlayerStateMachine.kt index 1fcfcb1..3310244 100644 --- a/app/src/main/java/com/nextcloud/client/media/PlayerStateMachine.kt +++ b/app/src/main/java/com/nextcloud/client/media/PlayerStateMachine.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media @@ -191,9 +191,7 @@ internal class PlayerStateMachine(initialState: State, private val delegate: Del * Contrary to [PlayerStateMachine.state] attribute, this method checks for * parent states. */ - fun isInState(state: State): Boolean { - return stateMachine.isInState(state) - } + fun isInState(state: State): Boolean = stateMachine.isInState(state) /** * Post state machine event to internal queue. diff --git a/app/src/main/java/com/nextcloud/client/media/PlaylistItem.kt b/app/src/main/java/com/nextcloud/client/media/PlaylistItem.kt index 616d2d1..2415145 100644 --- a/app/src/main/java/com/nextcloud/client/media/PlaylistItem.kt +++ b/app/src/main/java/com/nextcloud/client/media/PlaylistItem.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.media diff --git a/app/src/main/java/com/nextcloud/client/migrations/MigrationError.kt b/app/src/main/java/com/nextcloud/client/migrations/MigrationError.kt index 67aabec..b0c15c9 100644 --- a/app/src/main/java/com/nextcloud/client/migrations/MigrationError.kt +++ b/app/src/main/java/com/nextcloud/client/migrations/MigrationError.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations diff --git a/app/src/main/java/com/nextcloud/client/migrations/MigrationInfo.kt b/app/src/main/java/com/nextcloud/client/migrations/MigrationInfo.kt index 2e22728..135c7b9 100644 --- a/app/src/main/java/com/nextcloud/client/migrations/MigrationInfo.kt +++ b/app/src/main/java/com/nextcloud/client/migrations/MigrationInfo.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations diff --git a/app/src/main/java/com/nextcloud/client/migrations/Migrations.kt b/app/src/main/java/com/nextcloud/client/migrations/Migrations.kt index 31d4a0e..2c3724c 100644 --- a/app/src/main/java/com/nextcloud/client/migrations/Migrations.kt +++ b/app/src/main/java/com/nextcloud/client/migrations/Migrations.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations @@ -42,9 +42,7 @@ class Migrations @Inject constructor( * into [MigrationException] */ class Step(val id: Int, val description: String, val mandatory: Boolean = true, val run: (s: Step) -> Unit) { - override fun toString(): String { - return "Migration $id: $description" - } + override fun toString(): String = "Migration $id: $description" } /** diff --git a/app/src/main/java/com/nextcloud/client/migrations/MigrationsDb.kt b/app/src/main/java/com/nextcloud/client/migrations/MigrationsDb.kt index d7e0357..235f3cf 100644 --- a/app/src/main/java/com/nextcloud/client/migrations/MigrationsDb.kt +++ b/app/src/main/java/com/nextcloud/client/migrations/MigrationsDb.kt @@ -2,11 +2,12 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations import android.content.SharedPreferences +import androidx.core.content.edit import java.util.TreeSet class MigrationsDb(private val migrationsDb: SharedPreferences) { @@ -39,12 +40,12 @@ class MigrationsDb(private val migrationsDb: SharedPreferences) { addAll(oldApplied) addAll(migrations.map { it.toString() }) } - migrationsDb.edit().putStringSet(DB_KEY_APPLIED_MIGRATIONS, newApplied).apply() + migrationsDb.edit { putStringSet(DB_KEY_APPLIED_MIGRATIONS, newApplied) } } var lastMigratedVersion: Int set(value) { - migrationsDb.edit().putInt(DB_KEY_LAST_MIGRATED_VERSION, value).apply() + migrationsDb.edit { putInt(DB_KEY_LAST_MIGRATED_VERSION, value) } } get() { return migrationsDb.getInt(DB_KEY_LAST_MIGRATED_VERSION, NO_LAST_MIGRATED_VERSION) @@ -56,17 +57,17 @@ class MigrationsDb(private val migrationsDb: SharedPreferences) { fun setFailed(id: Int, error: String) { migrationsDb - .edit() - .putBoolean(DB_KEY_FAILED, true) - .putString(DB_KEY_FAILED_MIGRATION_ERROR_MESSAGE, error) - .putInt(DB_KEY_FAILED_MIGRATION_ID, id) - .apply() + .edit { + putBoolean(DB_KEY_FAILED, true) + .putString(DB_KEY_FAILED_MIGRATION_ERROR_MESSAGE, error) + .putInt(DB_KEY_FAILED_MIGRATION_ID, id) + } } fun clearMigrations() { - migrationsDb.edit() - .putStringSet(DB_KEY_APPLIED_MIGRATIONS, emptySet()) - .putInt(DB_KEY_LAST_MIGRATED_VERSION, 0) - .apply() + migrationsDb.edit { + putStringSet(DB_KEY_APPLIED_MIGRATIONS, emptySet()) + .putInt(DB_KEY_LAST_MIGRATED_VERSION, 0) + } } } diff --git a/app/src/main/java/com/nextcloud/client/migrations/MigrationsManager.kt b/app/src/main/java/com/nextcloud/client/migrations/MigrationsManager.kt index 5fc6b82..730762b 100644 --- a/app/src/main/java/com/nextcloud/client/migrations/MigrationsManager.kt +++ b/app/src/main/java/com/nextcloud/client/migrations/MigrationsManager.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations diff --git a/app/src/main/java/com/nextcloud/client/migrations/MigrationsManagerImpl.kt b/app/src/main/java/com/nextcloud/client/migrations/MigrationsManagerImpl.kt index d6afa00..eb7a7cc 100644 --- a/app/src/main/java/com/nextcloud/client/migrations/MigrationsManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/migrations/MigrationsManagerImpl.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations diff --git a/app/src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt b/app/src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt index 98406e2..6176146 100644 --- a/app/src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt +++ b/app/src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.mixins diff --git a/app/src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt b/app/src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt index c38e066..66a5446 100644 --- a/app/src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt +++ b/app/src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.mixins diff --git a/app/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt b/app/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt index 58bbcc4..1fff6f6 100644 --- a/app/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt +++ b/app/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.mixins @@ -25,10 +25,7 @@ import java.util.Optional * It is an intermediary step facilitating comprehensive rework of * account handling logic. */ -class SessionMixin( - private val activity: Activity, - private val accountManager: UserAccountManager -) : ActivityMixin { +class SessionMixin(private val activity: Activity, private val accountManager: UserAccountManager) : ActivityMixin { var currentAccount: Account = getDefaultAccount() private set @@ -51,12 +48,10 @@ class SessionMixin( setAccount(user.toPlatformAccount()) } - fun getUser(): Optional { - return if (currentAccount.isAnonymous(activity)) { - Optional.empty() - } else { - accountManager.getUser(currentAccount.name) - } + fun getUser(): Optional = if (currentAccount.isAnonymous(activity)) { + Optional.empty() + } else { + accountManager.getUser(currentAccount.name) } /** diff --git a/app/src/main/java/com/nextcloud/client/network/ClientFactory.java b/app/src/main/java/com/nextcloud/client/network/ClientFactory.java index 47770f7..a0780bd 100644 --- a/app/src/main/java/com/nextcloud/client/network/ClientFactory.java +++ b/app/src/main/java/com/nextcloud/client/network/ClientFactory.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network; diff --git a/app/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java b/app/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java index e753f98..22fe0c8 100644 --- a/app/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java +++ b/app/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network; diff --git a/app/src/main/java/com/nextcloud/client/network/Connectivity.kt b/app/src/main/java/com/nextcloud/client/network/Connectivity.kt index 11ac986..e3b4b19 100644 --- a/app/src/main/java/com/nextcloud/client/network/Connectivity.kt +++ b/app/src/main/java/com/nextcloud/client/network/Connectivity.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network diff --git a/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java b/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java index 2f97bae..7da4afe 100644 --- a/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java +++ b/app/src/main/java/com/nextcloud/client/network/ConnectivityService.java @@ -2,15 +2,29 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network; + +import androidx.annotation.NonNull; + /** * This service provides information about current network connectivity * and server reachability. */ public interface ConnectivityService { + /** + * Checks the availability of the server and the device's internet connection. + *

    + * This method performs a network request to verify if the server is accessible and + * checks if the device has an active internet connection. + *

    + * + * @param callback A callback to handle the result of the network and server availability check. + */ + void isNetworkAndServerAvailable(@NonNull GenericCallback callback); + boolean isConnected(); /** @@ -28,4 +42,13 @@ public interface ConnectivityService { * @return Network connectivity status in platform-agnostic format */ Connectivity getConnectivity(); + + /** + * Callback interface for asynchronous results. + * + * @param The type of result returned by the callback. + */ + interface GenericCallback { + void onComplete(T result); + } } diff --git a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java index f1582fb..713cea7 100644 --- a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java +++ b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java @@ -4,7 +4,7 @@ * @author Chris Narkiewicz * Copyright (C) 2021 Chris Narkiewicz * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network; @@ -13,6 +13,9 @@ import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; import com.nextcloud.client.account.Server; import com.nextcloud.client.account.UserAccountManager; @@ -22,6 +25,7 @@ import com.owncloud.android.lib.common.utils.Log_OC; import org.apache.commons.httpclient.HttpStatus; +import androidx.annotation.NonNull; import androidx.core.net.ConnectivityManagerCompat; import kotlin.jvm.functions.Function1; @@ -35,6 +39,7 @@ class ConnectivityServiceImpl implements ConnectivityService { private final ClientFactory clientFactory; private final GetRequestBuilder requestBuilder; private final WalledCheckCache walledCheckCache; + private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); static class GetRequestBuilder implements Function1 { @Override @@ -55,6 +60,24 @@ class ConnectivityServiceImpl implements ConnectivityService { this.walledCheckCache = walledCheckCache; } + @Override + public void isNetworkAndServerAvailable(@NonNull GenericCallback callback) { + new Thread(() -> { + Network activeNetwork = platformConnectivityManager.getActiveNetwork(); + NetworkCapabilities networkCapabilities = platformConnectivityManager.getNetworkCapabilities(activeNetwork); + boolean hasInternet = networkCapabilities != null && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + + boolean result; + if (hasInternet) { + result = !isInternetWalled(); + } else { + result = false; + } + + mainThreadHandler.post(() -> callback.onComplete(result)); + }).start(); + } + @Override public boolean isConnected() { Network nw = platformConnectivityManager.getActiveNetwork(); @@ -64,10 +87,21 @@ class ConnectivityServiceImpl implements ConnectivityService { return false; } - return actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || + if (actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) || - actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH); + actNw.hasTransport(NetworkCapabilities.TRANSPORT_VPN) || + actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) || + actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { + return true; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && + actNw.hasTransport(NetworkCapabilities.TRANSPORT_USB)) { + return true; + } + + return false; } @Override @@ -76,31 +110,26 @@ class ConnectivityServiceImpl implements ConnectivityService { if (cachedValue != null) { return cachedValue; } else { + Server server = accountManager.getUser().getServer(); + String baseServerAddress = server.getUri().toString(); + boolean result; Connectivity c = getConnectivity(); - if (c.isConnected() && c.isWifi() && !c.isMetered()) { + if (c != null && c.isConnected() && c.isWifi() && !c.isMetered() && !baseServerAddress.isEmpty()) { + GetMethod get = requestBuilder.invoke(baseServerAddress + CONNECTIVITY_CHECK_ROUTE); + PlainClient client = clientFactory.createPlainClient(); - Server server = accountManager.getUser().getServer(); - String baseServerAddress = server.getUri().toString(); - if (baseServerAddress.isEmpty()) { - result = true; - } else { + int status = get.execute(client); - GetMethod get = requestBuilder.invoke(baseServerAddress + CONNECTIVITY_CHECK_ROUTE); - PlainClient client = clientFactory.createPlainClient(); - - int status = get.execute(client); - - // Content-Length is not available when using chunked transfer encoding, so check for -1 as well - result = !(status == HttpStatus.SC_NO_CONTENT && get.getResponseContentLength() <= 0); - get.releaseConnection(); - if (result) { - Log_OC.w(TAG, "isInternetWalled(): Failed to GET " + CONNECTIVITY_CHECK_ROUTE + "," + - " assuming connectivity is impaired"); - } + // Content-Length is not available when using chunked transfer encoding, so check for -1 as well + result = !(status == HttpStatus.SC_NO_CONTENT && get.getResponseContentLength() <= 0); + get.releaseConnection(); + if (result) { + Log_OC.w(TAG, "isInternetWalled(): Failed to GET " + CONNECTIVITY_CHECK_ROUTE + "," + + " assuming connectivity is impaired"); } } else { - result = !c.isConnected(); + result = (c != null && !c.isConnected()); } walledCheckCache.setValue(result); diff --git a/app/src/main/java/com/nextcloud/client/network/NetworkModule.java b/app/src/main/java/com/nextcloud/client/network/NetworkModule.java index f0b9b9a..8fbca7e 100644 --- a/app/src/main/java/com/nextcloud/client/network/NetworkModule.java +++ b/app/src/main/java/com/nextcloud/client/network/NetworkModule.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network; diff --git a/app/src/main/java/com/nextcloud/client/network/WalledCheckCache.kt b/app/src/main/java/com/nextcloud/client/network/WalledCheckCache.kt index cac5b40..7246732 100644 --- a/app/src/main/java/com/nextcloud/client/network/WalledCheckCache.kt +++ b/app/src/main/java/com/nextcloud/client/network/WalledCheckCache.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network @@ -17,13 +17,11 @@ class WalledCheckCache @Inject constructor(private val clock: Clock) { private var cachedEntry: Pair? = null @Synchronized - fun isExpired(): Boolean { - return when (val timestamp = cachedEntry?.first) { - null -> true - else -> { - val diff = clock.currentTime - timestamp - diff >= CACHE_TIME_MS - } + fun isExpired(): Boolean = when (val timestamp = cachedEntry?.first) { + null -> true + else -> { + val diff = clock.currentTime - timestamp + diff >= CACHE_TIME_MS } } @@ -33,11 +31,9 @@ class WalledCheckCache @Inject constructor(private val clock: Clock) { } @Synchronized - fun getValue(): Boolean? { - return when (isExpired()) { - true -> null - else -> cachedEntry?.second - } + fun getValue(): Boolean? = when (isExpired()) { + true -> null + else -> cachedEntry?.second } @Synchronized diff --git a/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt b/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt index 7c99423..6ee8e6e 100644 --- a/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020-2021 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.notifications diff --git a/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt b/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt index 78f31f8..2aed1d2 100644 --- a/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020-2021 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.notifications @@ -13,7 +13,6 @@ import android.content.Context import android.content.Intent import android.content.res.Resources import android.graphics.BitmapFactory -import android.os.Build import androidx.core.app.NotificationCompat import com.nextcloud.client.account.User import com.owncloud.android.R @@ -38,11 +37,8 @@ class AppNotificationManagerImpl @Inject constructor( } private fun builder(channelId: String): NotificationCompat.Builder { - val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val builder = NotificationCompat.Builder(context, channelId) - } else { - NotificationCompat.Builder(context) - } viewThemeUtils.androidx.themeNotificationCompatBuilder(context, builder) return builder } diff --git a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt index 6c80514..824e7b2 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2018 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.onboarding @@ -18,12 +18,13 @@ import androidx.activity.OnBackPressedCallback import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2 import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.appinfo.AppInfo import com.nextcloud.client.di.Injectable import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.BuildConfig import com.owncloud.android.R import com.owncloud.android.authentication.AuthenticatorActivity @@ -39,7 +40,9 @@ import javax.inject.Inject /** * Activity displaying general feature after a fresh install. */ -class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injectable { +class FirstRunActivity : + BaseActivity(), + Injectable { @JvmField @Inject @@ -76,13 +79,12 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta binding = FirstRunActivityBinding.inflate(layoutInflater) setContentView(binding.root) - val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation) setSlideshowSize(resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) registerActivityResult() setupLoginButton() - setupSignupButton(isProviderOrOwnInstallationVisible) - setupHostOwnServerTextView(isProviderOrOwnInstallationVisible) + setupSignupButton(MDMConfig.showIntro(this)) + setupHostOwnServerTextView(MDMConfig.showIntro(this)) deleteAccountAtFirstLaunch() setupFeaturesViewAdapter() handleOnBackPressed() @@ -90,7 +92,7 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta private fun applyDefaultTheme() { defaultViewThemeUtils = viewThemeUtilsFactory?.withPrimaryAsBackground() - defaultViewThemeUtils?.platform?.themeStatusBar(this, ColorRole.PRIMARY) + defaultViewThemeUtils?.platform?.colorStatusBar(this, resources.getColor(R.color.primary)) } private fun registerActivityResult() { @@ -171,10 +173,14 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta @Suppress("SpreadOperator") private fun setupFeaturesViewAdapter() { - val featuresViewAdapter = FeaturesViewAdapter(supportFragmentManager, *firstRun) - binding.progressIndicator.setNumberOfSteps(featuresViewAdapter.count) + val featuresViewAdapter = FeaturesViewAdapter(this, *firstRun) + binding.progressIndicator.setNumberOfSteps(featuresViewAdapter.itemCount) binding.contentPanel.adapter = featuresViewAdapter - binding.contentPanel.addOnPageChangeListener(this) + binding.contentPanel.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + binding.progressIndicator.animateToStep(position + 1) + } + }) } private fun handleOnBackPressed() { @@ -203,10 +209,9 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta } private fun setSlideshowSize(isLandscape: Boolean) { - val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation) binding.buttonLayout.orientation = if (isLandscape) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL - val layoutParams: LinearLayout.LayoutParams = if (isProviderOrOwnInstallationVisible) { + val layoutParams: LinearLayout.LayoutParams = if (MDMConfig.showIntro(this)) { LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT @@ -236,18 +241,6 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta super.onStop() } - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { - // unused but to be implemented due to abstract parent - } - - override fun onPageSelected(position: Int) { - binding.progressIndicator.animateToStep(position + 1) - } - - override fun onPageScrollStateChanged(state: Int) { - // unused but to be implemented due to abstract parent - } - companion object { const val EXTRA_ALLOW_CLOSE = "ALLOW_CLOSE" const val EXTRA_EXIT = "EXIT" diff --git a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingModule.kt b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingModule.kt index e6e6fe6..b97382f 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingModule.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingModule.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.onboarding @@ -22,7 +22,5 @@ class OnboardingModule { resources: Resources, preferences: AppPreferences, accountProvider: CurrentAccountProvider - ): OnboardingService { - return OnboardingServiceImpl(resources, preferences, accountProvider) - } + ): OnboardingService = OnboardingServiceImpl(resources, preferences, accountProvider) } diff --git a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingService.kt b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingService.kt index 7332c56..e75955f 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingService.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingService.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.onboarding diff --git a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt index 129da4b..ceaf911 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt @@ -2,7 +2,8 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.onboarding @@ -12,13 +13,14 @@ import android.content.Intent import android.content.res.Resources import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.BuildConfig import com.owncloud.android.R import com.owncloud.android.authentication.AuthenticatorActivity import com.owncloud.android.features.FeatureItem import com.owncloud.android.ui.activity.PassCodeActivity -internal class OnboardingServiceImpl constructor( +internal class OnboardingServiceImpl( private val resources: Resources, private val preferences: AppPreferences, private val accountProvider: CurrentAccountProvider @@ -42,12 +44,11 @@ internal class OnboardingServiceImpl constructor( override val isFirstRun: Boolean get() { - return accountProvider.currentAccount == null + return accountProvider.user.isAnonymous } - override fun shouldShowWhatsNew(callingContext: Context): Boolean { - return callingContext !is PassCodeActivity && whatsNew.isNotEmpty() - } + override fun shouldShowWhatsNew(callingContext: Context): Boolean = + callingContext !is PassCodeActivity && whatsNew.isNotEmpty() override fun launchActivityIfNeeded(activity: Activity) { if (!resources.getBoolean(R.bool.show_whats_new) || activity is WhatsNewActivity) { @@ -60,8 +61,7 @@ internal class OnboardingServiceImpl constructor( } override fun launchFirstRunIfNeeded(activity: Activity): Boolean { - val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation) - val canLaunch = isProviderOrOwnInstallationVisible && isFirstRun && activity is AuthenticatorActivity + val canLaunch = MDMConfig.showIntro(activity) && isFirstRun && activity is AuthenticatorActivity if (canLaunch) { val intent = Intent(activity, FirstRunActivity::class.java) activity.startActivityForResult(intent, AuthenticatorActivity.REQUEST_CODE_FIRST_RUN) diff --git a/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt index f62168e..a824c7c 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt @@ -1,11 +1,11 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2016 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.onboarding @@ -13,7 +13,7 @@ import android.os.Bundle import android.view.View import androidx.activity.OnBackPressedCallback import androidx.fragment.app.FragmentActivity -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.appinfo.AppInfo import com.nextcloud.client.di.Injectable @@ -29,7 +29,9 @@ import javax.inject.Inject /** * Activity displaying new features after an update. */ -class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Injectable { +class WhatsNewActivity : + FragmentActivity(), + Injectable { @JvmField @Inject @@ -64,7 +66,11 @@ class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Inj val showWebView = urls.isNotEmpty() setupFeatureViewAdapter(showWebView, urls) - binding.contentPanel.addOnPageChangeListener(this) + binding.contentPanel.registerOnPageChangeCallback(object : OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + controlPanelOnPageSelected(position) + } + }) setupForwardImageButton() setupSkipImageButton() setupWelcomeText(showWebView) @@ -75,15 +81,15 @@ class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Inj @Suppress("SpreadOperator") private fun setupFeatureViewAdapter(showWebView: Boolean, urls: Array) { val adapter = if (showWebView) { - FeaturesWebViewAdapter(supportFragmentManager, *urls) + FeaturesWebViewAdapter(this, *urls) } else { onboarding?.let { - FeaturesViewAdapter(supportFragmentManager, *it.whatsNew) + FeaturesViewAdapter(this, *it.whatsNew) } } adapter?.let { - binding.progressIndicator.setNumberOfSteps(it.count) + binding.progressIndicator.setNumberOfSteps(it.itemCount) binding.contentPanel.adapter = it } } @@ -142,14 +148,8 @@ class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Inj preferences?.lastSeenVersionCode = BuildConfig.VERSION_CODE } - override fun onPageSelected(position: Int) { + private fun controlPanelOnPageSelected(position: Int) { binding.progressIndicator.animateToStep(position + 1) updateNextButtonIfNeeded() } - - @Suppress("EmptyFunctionBlock") - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} - - @Suppress("EmptyFunctionBlock") - override fun onPageScrollStateChanged(state: Int) {} } diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java index b2517b5..610ab2c 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java @@ -167,14 +167,6 @@ public interface AppPreferences { */ String[] getPassCode(); - /** - * Gets the unlock via fingerprint preference configured by the user. - * - * @implNote this is always false - * @return useFingerprint is unlock with fingerprint enabled - */ - boolean isFingerprintUnlockEnabled(); - /** * Gets the auto upload paths flag last set. * @@ -210,7 +202,7 @@ public interface AppPreferences { * Get preferred folder sort order. * * @param folder Folder whoch order is being retrieved or null for root folder - * @return sort order the sort order, default is {@link FileSortOrder#sort_a_to_z} (sort by name) + * @return sort order the sort order, default is {@link FileSortOrder# sort_a_to_z} (sort by name) */ FileSortOrder getSortOrderByFolder(@Nullable OCFile folder); @@ -232,7 +224,7 @@ public interface AppPreferences { /** * Get preferred folder sort order. * - * @return sort order the sort order, default is {@link FileSortOrder#sort_a_to_z} (sort by name) + * @return sort order the sort order, default is {@link FileSortOrder# sort_a_to_z} (sort by name) */ FileSortOrder getSortOrderByType(FileSortOrder.Type type, FileSortOrder defaultOrder); FileSortOrder getSortOrderByType(FileSortOrder.Type type); @@ -391,4 +383,19 @@ public interface AppPreferences { @NonNull String getLastSelectedMediaFolder(); + + void setTwoWaySyncStatus(boolean value); + boolean isTwoWaySyncEnabled(); + + void setTwoWaySyncInterval(Long value); + Long getTwoWaySyncInterval(); + + boolean shouldStopDownloadJobsOnStart(); + void setStopDownloadJobsOnStart(boolean value); + + int getPassCodeDelay(); + void setPassCodeDelay(int value); + + String getLastDisplayedAccountName(); + void setLastDisplayedAccountName(String lastDisplayedAccountName); } diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index 4531fd2..8806b5a 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2016 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.preferences; @@ -55,6 +55,7 @@ public final class AppPreferencesImpl implements AppPreferences { */ public static final String AUTO_PREF__LAST_SEEN_VERSION_CODE = "lastSeenVersionCode"; public static final String STORAGE_PATH = "storage_path"; + public static final String DATA_STORAGE_LOCATION = "data_storage_location"; public static final String STORAGE_PATH_VALID = "storage_path_valid"; public static final String PREF__DARK_THEME = "dark_theme_mode"; public static final float DEFAULT_GRID_COLUMN = 3f; @@ -102,6 +103,15 @@ public final class AppPreferencesImpl implements AppPreferences { private static final String PREF__STORAGE_PERMISSION_REQUESTED = "storage_permission_requested"; private static final String PREF__IN_APP_REVIEW_DATA = "in_app_review_data"; + private static final String PREF__TWO_WAY_STATUS = "two_way_sync_status"; + private static final String PREF__TWO_WAY_SYNC_INTERVAL = "two_way_sync_interval"; + + private static final String PREF__STOP_DOWNLOAD_JOBS_ON_START = "stop_download_jobs_on_start"; + + private static final String PREF__PASSCODE_DELAY_IN_SECONDS = "passcode_delay_in_seconds"; + + private static final String PREF_LAST_DISPLAYED_ACCOUNT_NAME = "last_displayed_user"; + private static final String LOG_ENTRY = "log_entry"; private final Context context; @@ -300,11 +310,6 @@ public final class AppPreferencesImpl implements AppPreferences { }; } - @Override - public boolean isFingerprintUnlockEnabled() { - return preferences.getBoolean(SettingsActivity.PREFERENCE_USE_FINGERPRINT, false); - } - @Override public String getFolderLayout(OCFile folder) { return getFolderPreference(context, @@ -329,7 +334,7 @@ public final class AppPreferencesImpl implements AppPreferences { userAccountManager.getUser(), PREF__FOLDER_SORT_ORDER, folder, - FileSortOrder.sort_a_to_z.name)); + FileSortOrder.SORT_A_TO_Z.name)); } @Override @@ -343,7 +348,7 @@ public final class AppPreferencesImpl implements AppPreferences { @Override public FileSortOrder getSortOrderByType(FileSortOrder.Type type) { - return getSortOrderByType(type, FileSortOrder.sort_a_to_z); + return getSortOrderByType(type, FileSortOrder.SORT_A_TO_Z); } @Override @@ -789,4 +794,54 @@ public final class AppPreferencesImpl implements AppPreferences { public String getLastSelectedMediaFolder() { return preferences.getString(PREF__MEDIA_FOLDER_LAST_PATH, OCFile.ROOT_PATH); } + + @Override + public void setTwoWaySyncStatus(boolean value) { + preferences.edit().putBoolean(PREF__TWO_WAY_STATUS, value).apply(); + } + + @Override + public boolean isTwoWaySyncEnabled() { + return preferences.getBoolean(PREF__TWO_WAY_STATUS, true); + } + + @Override + public void setTwoWaySyncInterval(Long value) { + preferences.edit().putLong(PREF__TWO_WAY_SYNC_INTERVAL, value).apply(); + } + + @Override + public Long getTwoWaySyncInterval() { + return preferences.getLong(PREF__TWO_WAY_SYNC_INTERVAL, 15L); + } + + @Override + public boolean shouldStopDownloadJobsOnStart() { + return preferences.getBoolean(PREF__STOP_DOWNLOAD_JOBS_ON_START, true); + } + + @Override + public void setStopDownloadJobsOnStart(boolean value) { + preferences.edit().putBoolean(PREF__STOP_DOWNLOAD_JOBS_ON_START, value).apply(); + } + + @Override + public int getPassCodeDelay() { + return preferences.getInt(PREF__PASSCODE_DELAY_IN_SECONDS, 0); + } + + @Override + public void setPassCodeDelay(int value) { + preferences.edit().putInt(PREF__PASSCODE_DELAY_IN_SECONDS, value).apply(); + } + + @Override + public String getLastDisplayedAccountName() { + return preferences.getString(PREF_LAST_DISPLAYED_ACCOUNT_NAME, null); + } + + @Override + public void setLastDisplayedAccountName(String lastDisplayedAccountName) { + preferences.edit().putString(PREF_LAST_DISPLAYED_ACCOUNT_NAME, lastDisplayedAccountName).apply(); + } } diff --git a/app/src/main/java/com/nextcloud/client/preferences/DarkMode.java b/app/src/main/java/com/nextcloud/client/preferences/DarkMode.java index 1bf454c..c516893 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/DarkMode.java +++ b/app/src/main/java/com/nextcloud/client/preferences/DarkMode.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.preferences; diff --git a/app/src/main/java/com/nextcloud/client/preferences/PreferencesModule.java b/app/src/main/java/com/nextcloud/client/preferences/PreferencesModule.java index d836dbb..09f4a8a 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/PreferencesModule.java +++ b/app/src/main/java/com/nextcloud/client/preferences/PreferencesModule.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.preferences; diff --git a/app/src/main/java/com/nextcloud/client/preferences/SubFolderRule.kt b/app/src/main/java/com/nextcloud/client/preferences/SubFolderRule.kt index b617470..8541203 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/SubFolderRule.kt +++ b/app/src/main/java/com/nextcloud/client/preferences/SubFolderRule.kt @@ -5,11 +5,13 @@ * Copyright (C) 2023 Dean Birch * Copyright (C) 2023 Nextcloud GmbH * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.preferences enum class SubFolderRule { - YEAR_MONTH, YEAR, YEAR_MONTH_DAY + YEAR_MONTH, + YEAR, + YEAR_MONTH_DAY } diff --git a/app/src/main/java/com/nextcloud/client/utils/IntentUtil.kt b/app/src/main/java/com/nextcloud/client/utils/IntentUtil.kt index 19e2d20..1f3dfa2 100644 --- a/app/src/main/java/com/nextcloud/client/utils/IntentUtil.kt +++ b/app/src/main/java/com/nextcloud/client/utils/IntentUtil.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.utils @@ -15,25 +15,22 @@ import com.owncloud.android.datamodel.OCFile object IntentUtil { @JvmStatic - public fun createSendIntent(context: Context, file: OCFile): Intent = - createBaseSendFileIntent().apply { - action = Intent.ACTION_SEND - type = file.mimeType - putExtra(Intent.EXTRA_STREAM, file.getExposedFileUri(context)) - } + public fun createSendIntent(context: Context, file: OCFile): Intent = createBaseSendFileIntent().apply { + action = Intent.ACTION_SEND + type = file.mimeType + putExtra(Intent.EXTRA_STREAM, file.getExposedFileUri(context)) + } @JvmStatic - public fun createSendIntent(context: Context, files: Array): Intent = - createBaseSendFileIntent().apply { - action = Intent.ACTION_SEND_MULTIPLE - type = getUniqueMimetype(files) - putParcelableArrayListExtra(Intent.EXTRA_STREAM, getExposedFileUris(context, files)) - } + public fun createSendIntent(context: Context, files: Array): Intent = createBaseSendFileIntent().apply { + action = Intent.ACTION_SEND_MULTIPLE + type = getUniqueMimetype(files) + putParcelableArrayListExtra(Intent.EXTRA_STREAM, getExposedFileUris(context, files)) + } - private fun createBaseSendFileIntent(): Intent = - Intent().apply { - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } + private fun createBaseSendFileIntent(): Intent = Intent().apply { + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } private fun getUniqueMimetype(files: Array): String? = when { files.distinctBy { it.mimeType }.size > 1 -> "*/*" diff --git a/app/src/main/java/com/nextcloud/client/utils/Throttler.kt b/app/src/main/java/com/nextcloud/client/utils/Throttler.kt index 4669c2d..3e54772 100644 --- a/app/src/main/java/com/nextcloud/client/utils/Throttler.kt +++ b/app/src/main/java/com/nextcloud/client/utils/Throttler.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Álvaro Brey * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.utils diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt index 13defe1..57b5e2a 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.widget diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationInterface.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationInterface.kt index c436f6d..f62adca 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationInterface.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationInterface.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.widget diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt index 61e807d..18fc207 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.widget diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt index a074c72..a55157d 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.widget @@ -11,33 +11,25 @@ import android.appwidget.AppWidgetManager import android.content.Context import android.content.Intent import android.graphics.Bitmap -import android.net.Uri import android.view.View import android.widget.RemoteViews import android.widget.RemoteViewsService -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.model.StreamEncoder -import com.bumptech.glide.load.resource.file.FileToStreamDecoder -import com.bumptech.glide.request.FutureTarget +import androidx.core.graphics.drawable.toBitmap +import androidx.core.net.toUri import com.nextcloud.android.lib.resources.dashboard.DashboardGetWidgetItemsRemoteOperation import com.nextcloud.android.lib.resources.dashboard.DashboardWidgetItem import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.network.ClientFactory +import com.nextcloud.utils.GlideHelper import com.owncloud.android.R +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.utils.BitmapUtils -import com.owncloud.android.utils.DisplayUtils.SVG_SIZE -import com.owncloud.android.utils.glide.CustomGlideStreamLoader -import com.owncloud.android.utils.glide.CustomGlideUriLoader -import com.owncloud.android.utils.svg.SVGorImage -import com.owncloud.android.utils.svg.SvgOrImageBitmapTranscoder -import com.owncloud.android.utils.svg.SvgOrImageDecoder import dagger.android.AndroidInjection import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.io.InputStream +import kotlinx.coroutines.withContext import javax.inject.Inject class DashboardWidgetService : RemoteViewsService() { @@ -55,15 +47,13 @@ class DashboardWidgetService : RemoteViewsService() { AndroidInjection.inject(this) } - override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { - return StackRemoteViewsFactory( - this.applicationContext, - userAccountManager, - clientFactory, - intent, - widgetRepository - ) - } + override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = StackRemoteViewsFactory( + this.applicationContext, + userAccountManager, + clientFactory, + intent, + widgetRepository + ) } class StackRemoteViewsFactory( @@ -123,29 +113,24 @@ class StackRemoteViewsFactory( widgetItems = emptyList() } - override fun getCount(): Int { - return if (hasLoadMore && widgetItems.isNotEmpty()) { - widgetItems.size + 1 - } else { - widgetItems.size - } + override fun getCount(): Int = if (hasLoadMore && widgetItems.isNotEmpty()) { + widgetItems.size + 1 + } else { + widgetItems.size } - override fun getViewAt(position: Int): RemoteViews { - return if (position == widgetItems.size) { - createLoadMoreView() - } else { - createItemView(position) - } + override fun getViewAt(position: Int): RemoteViews = if (position == widgetItems.size) { + createLoadMoreView() + } else { + createItemView(position) } - private fun createLoadMoreView(): RemoteViews { - return RemoteViews(context.packageName, R.layout.widget_item_load_more).apply { - val clickIntent = Intent(Intent.ACTION_VIEW, Uri.parse(widgetConfiguration.moreButton?.link)) + private fun createLoadMoreView(): RemoteViews = + RemoteViews(context.packageName, R.layout.widget_item_load_more).apply { + val clickIntent = Intent(Intent.ACTION_VIEW, widgetConfiguration.moreButton?.link?.toUri()) setTextViewText(R.id.load_more, widgetConfiguration.moreButton?.text) setOnClickFillInIntent(R.id.load_more_container, clickIntent) } - } // we will switch soon to coil and then streamline all of this // Kotlin cannot catch multiple exception types at same time @@ -165,60 +150,42 @@ class StackRemoteViewsFactory( updateTexts(widgetItem, this) if (widgetItem.link.isNotEmpty()) { - val clickIntent = Intent(Intent.ACTION_VIEW, Uri.parse(widgetItem.link)) + val clickIntent = Intent(Intent.ACTION_VIEW, widgetItem.link.toUri()) setOnClickFillInIntent(R.id.text_container, clickIntent) } } } - @Suppress("TooGenericExceptionCaught") private fun loadIcon(widgetItem: DashboardWidgetItem, remoteViews: RemoteViews) { - val isIconSVG = Uri.parse(widgetItem.iconUrl).encodedPath!!.endsWith(".svg") - val source: FutureTarget = if (isIconSVG) { - loadSVGIcon(widgetItem) - } else { - loadBitmapIcon(widgetItem) - } + CoroutineScope(Dispatchers.IO).launch { + val client = OwnCloudClientManagerFactory.getDefaultSingleton() + .getNextcloudClientFor(userAccountManager.user.toOwnCloudAccount(), context) + val pictureDrawable = GlideHelper.getDrawable(context, client, widgetItem.iconUrl) + val bitmap = pictureDrawable?.toBitmap() ?: return@launch + withContext(Dispatchers.Main) { + remoteViews.setRemoteImageView(bitmap) + return@withContext + } + } + } + + @Suppress("TooGenericExceptionCaught") + private fun RemoteViews.setRemoteImageView(source: Bitmap) { try { val bitmap: Bitmap = if (widgetConfiguration.roundIcon) { - BitmapUtils.roundBitmap(source.get()) + BitmapUtils.roundBitmap(source) } else { - source.get() + source } - remoteViews.setImageViewBitmap(R.id.icon, bitmap) + setImageViewBitmap(R.id.icon, bitmap) } catch (e: Exception) { Log_OC.d(TAG, "Error setting icon", e) - remoteViews.setImageViewResource(R.id.icon, R.drawable.ic_dashboard) + setImageViewResource(R.id.icon, R.drawable.ic_dashboard) } } - private fun loadSVGIcon(widgetItem: DashboardWidgetItem): FutureTarget { - return Glide.with(context) - .using( - CustomGlideUriLoader(userAccountManager.user, clientFactory), - InputStream::class.java - ) - .from(Uri::class.java) - .`as`(SVGorImage::class.java) - .transcode(SvgOrImageBitmapTranscoder(SVG_SIZE, SVG_SIZE), Bitmap::class.java) - .sourceEncoder(StreamEncoder()) - .cacheDecoder(FileToStreamDecoder(SvgOrImageDecoder())) - .decoder(SvgOrImageDecoder()) - .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .load(Uri.parse(widgetItem.iconUrl)) - .into(SVG_SIZE, SVG_SIZE) - } - - private fun loadBitmapIcon(widgetItem: DashboardWidgetItem): FutureTarget { - return Glide.with(context) - .using(CustomGlideStreamLoader(widgetConfiguration.user.get(), clientFactory)) - .load(widgetItem.iconUrl) - .asBitmap() - .into(SVG_SIZE, SVG_SIZE) - } - private fun updateTexts(widgetItem: DashboardWidgetItem, remoteViews: RemoteViews) { remoteViews.setTextViewText(R.id.title, widgetItem.title) @@ -230,25 +197,17 @@ class StackRemoteViewsFactory( } } - override fun getLoadingView(): RemoteViews? { - return null + override fun getLoadingView(): RemoteViews? = null + + override fun getViewTypeCount(): Int = if (hasLoadMore) { + 2 + } else { + 1 } - override fun getViewTypeCount(): Int { - return if (hasLoadMore) { - 2 - } else { - 1 - } - } + override fun getItemId(position: Int): Long = position.toLong() - override fun getItemId(position: Int): Long { - return position.toLong() - } - - override fun hasStableIds(): Boolean { - return true - } + override fun hasStableIds(): Boolean = true companion object { private val TAG = DashboardWidgetService::class.simpleName diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt index 9806202..3430664 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.widget @@ -11,28 +11,23 @@ import android.app.PendingIntent import android.appwidget.AppWidgetManager import android.content.Context import android.content.Intent -import android.graphics.Bitmap -import android.net.Uri import android.os.Build import android.view.View import android.widget.RemoteViews -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.model.StreamEncoder -import com.bumptech.glide.load.resource.file.FileToStreamDecoder -import com.bumptech.glide.request.animation.GlideAnimation +import androidx.core.graphics.drawable.toBitmap +import androidx.core.net.toUri import com.bumptech.glide.request.target.AppWidgetTarget import com.nextcloud.android.lib.resources.dashboard.DashboardButton import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.network.ClientFactory +import com.nextcloud.utils.GlideHelper import com.owncloud.android.R +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.utils.BitmapUtils -import com.owncloud.android.utils.DisplayUtils.SVG_SIZE -import com.owncloud.android.utils.glide.CustomGlideUriLoader -import com.owncloud.android.utils.svg.SVGorImage -import com.owncloud.android.utils.svg.SvgOrImageBitmapTranscoder -import com.owncloud.android.utils.svg.SvgOrImageDecoder -import java.io.InputStream +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject class DashboardWidgetUpdater @Inject constructor( @@ -50,7 +45,7 @@ class DashboardWidgetUpdater @Inject constructor( ) { val intent = Intent(context, DashboardWidgetService::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) - data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) + data = toUri(Intent.URI_INTENT_SCHEME).toUri() } val views = RemoteViews(context.packageName, R.layout.dashboard_widget).apply { @@ -61,7 +56,10 @@ class DashboardWidgetUpdater @Inject constructor( setAddButton(addButton, appWidgetId, this) setPendingReload(this, appWidgetId) setPendingClick(this) - loadIcon(appWidgetId, iconUrl, this) + + if (iconUrl.isNotEmpty()) { + loadIcon(appWidgetId, iconUrl, this) + } } appWidgetManager.run { @@ -130,7 +128,7 @@ class DashboardWidgetUpdater @Inject constructor( val intent = Intent(context, DashboardWidgetProvider::class.java).apply { setPackage(context.packageName) action = DashboardWidgetProvider.OPEN_INTENT - data = Uri.parse(addButton.link) + data = addButton.link.toUri() } return PendingIntent.getBroadcast( @@ -156,28 +154,17 @@ class DashboardWidgetUpdater @Inject constructor( // endregion private fun loadIcon(appWidgetId: Int, iconUrl: String, remoteViews: RemoteViews) { - val iconTarget = object : AppWidgetTarget(context, remoteViews, R.id.icon, appWidgetId) { - override fun onResourceReady(resource: Bitmap?, glideAnimation: GlideAnimation?) { - if (resource != null) { - val tintedBitmap = BitmapUtils.tintImage(resource, R.color.black) - super.onResourceReady(tintedBitmap, glideAnimation) - } + val target = AppWidgetTarget(context, R.id.icon, remoteViews, appWidgetId) + CoroutineScope(Dispatchers.IO).launch { + val client = OwnCloudClientManagerFactory.getDefaultSingleton() + .getNextcloudClientFor(accountProvider.user.toOwnCloudAccount(), context) + val drawable = GlideHelper.getDrawable(context, client, iconUrl) + val bitmap = drawable?.toBitmap() ?: return@launch + val tintedBitmap = BitmapUtils.tintImage(bitmap, R.color.black) + + withContext(Dispatchers.Main) { + target.onResourceReady(tintedBitmap, null) } } - - Glide.with(context) - .using( - CustomGlideUriLoader(accountProvider.user, clientFactory), - InputStream::class.java - ) - .from(Uri::class.java) - .`as`(SVGorImage::class.java) - .transcode(SvgOrImageBitmapTranscoder(SVG_SIZE, SVG_SIZE), Bitmap::class.java) - .sourceEncoder(StreamEncoder()) - .cacheDecoder(FileToStreamDecoder(SvgOrImageDecoder())) - .decoder(SvgOrImageDecoder()) - .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .load(Uri.parse(iconUrl)) - .into(iconTarget) } } diff --git a/app/src/main/java/com/nextcloud/client/widget/WidgetConfiguration.kt b/app/src/main/java/com/nextcloud/client/widget/WidgetConfiguration.kt index 49503fe..89b5ea3 100644 --- a/app/src/main/java/com/nextcloud/client/widget/WidgetConfiguration.kt +++ b/app/src/main/java/com/nextcloud/client/widget/WidgetConfiguration.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.widget diff --git a/app/src/main/java/com/nextcloud/client/widget/WidgetRepository.kt b/app/src/main/java/com/nextcloud/client/widget/WidgetRepository.kt index da12dd4..c72f1fb 100644 --- a/app/src/main/java/com/nextcloud/client/widget/WidgetRepository.kt +++ b/app/src/main/java/com/nextcloud/client/widget/WidgetRepository.kt @@ -3,11 +3,12 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.widget import android.content.SharedPreferences +import androidx.core.content.edit import com.nextcloud.android.lib.resources.dashboard.DashBoardButtonType import com.nextcloud.android.lib.resources.dashboard.DashboardButton import com.nextcloud.android.lib.resources.dashboard.DashboardWidget @@ -21,48 +22,48 @@ class WidgetRepository @Inject constructor( val preferences: SharedPreferences ) { fun saveWidget(widgetId: Int, widget: DashboardWidget, user: User) { - val editor: SharedPreferences.Editor = preferences - .edit() - .putString(PREF__WIDGET_ID + widgetId, widget.id) - .putString(PREF__WIDGET_TITLE + widgetId, widget.title) - .putString(PREF__WIDGET_ICON + widgetId, widget.iconUrl) - .putBoolean(PREF__WIDGET_ROUND_ICON + widgetId, widget.roundIcons) - .putString(PREF__WIDGET_USER + widgetId, user.accountName) - val buttonList = widget.buttons - if (buttonList != null && buttonList.isNotEmpty()) { - for (button in buttonList) { - if (button.type == DashBoardButtonType.NEW) { - editor - .putString(PREF__WIDGET_ADD_BUTTON_TYPE + widgetId, button.type.toString()) - .putString(PREF__WIDGET_ADD_BUTTON_URL + widgetId, button.link) - .putString(PREF__WIDGET_ADD_BUTTON_TEXT + widgetId, button.text) - } - if (button.type == DashBoardButtonType.MORE) { - editor - .putString(PREF__WIDGET_MORE_BUTTON_TYPE + widgetId, button.type.toString()) - .putString(PREF__WIDGET_MORE_BUTTON_URL + widgetId, button.link) - .putString(PREF__WIDGET_MORE_BUTTON_TEXT + widgetId, button.text) + preferences + .edit { + putString(PREF__WIDGET_ID + widgetId, widget.id) + .putString(PREF__WIDGET_TITLE + widgetId, widget.title) + .putString(PREF__WIDGET_ICON + widgetId, widget.iconUrl) + .putBoolean(PREF__WIDGET_ROUND_ICON + widgetId, widget.roundIcons) + .putString(PREF__WIDGET_USER + widgetId, user.accountName) + val buttonList = widget.buttons + if (!buttonList.isNullOrEmpty()) { + for (button in buttonList) { + if (button.type == DashBoardButtonType.NEW) { + this + .putString(PREF__WIDGET_ADD_BUTTON_TYPE + widgetId, button.type.toString()) + .putString(PREF__WIDGET_ADD_BUTTON_URL + widgetId, button.link) + .putString(PREF__WIDGET_ADD_BUTTON_TEXT + widgetId, button.text) + } + if (button.type == DashBoardButtonType.MORE) { + this + .putString(PREF__WIDGET_MORE_BUTTON_TYPE + widgetId, button.type.toString()) + .putString(PREF__WIDGET_MORE_BUTTON_URL + widgetId, button.link) + .putString(PREF__WIDGET_MORE_BUTTON_TEXT + widgetId, button.text) + } + } } } - } - editor.apply() } fun deleteWidget(widgetId: Int) { preferences - .edit() - .remove(PREF__WIDGET_ID + widgetId) - .remove(PREF__WIDGET_TITLE + widgetId) - .remove(PREF__WIDGET_ICON + widgetId) - .remove(PREF__WIDGET_ROUND_ICON + widgetId) - .remove(PREF__WIDGET_USER + widgetId) - .remove(PREF__WIDGET_ADD_BUTTON_TEXT + widgetId) - .remove(PREF__WIDGET_ADD_BUTTON_URL + widgetId) - .remove(PREF__WIDGET_ADD_BUTTON_TYPE + widgetId) - .remove(PREF__WIDGET_MORE_BUTTON_TEXT + widgetId) - .remove(PREF__WIDGET_MORE_BUTTON_URL + widgetId) - .remove(PREF__WIDGET_MORE_BUTTON_TYPE + widgetId) - .apply() + .edit { + remove(PREF__WIDGET_ID + widgetId) + .remove(PREF__WIDGET_TITLE + widgetId) + .remove(PREF__WIDGET_ICON + widgetId) + .remove(PREF__WIDGET_ROUND_ICON + widgetId) + .remove(PREF__WIDGET_USER + widgetId) + .remove(PREF__WIDGET_ADD_BUTTON_TEXT + widgetId) + .remove(PREF__WIDGET_ADD_BUTTON_URL + widgetId) + .remove(PREF__WIDGET_ADD_BUTTON_TYPE + widgetId) + .remove(PREF__WIDGET_MORE_BUTTON_TEXT + widgetId) + .remove(PREF__WIDGET_MORE_BUTTON_URL + widgetId) + .remove(PREF__WIDGET_MORE_BUTTON_TYPE + widgetId) + } } fun getWidget(widgetId: Int): WidgetConfiguration { diff --git a/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt b/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt index 1bdd7b4..05a44d6 100644 --- a/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt +++ b/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt @@ -1,13 +1,14 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.model @Suppress("MagicNumber") enum class HTTPStatusCodes(val code: Int) { + SUCCESS(200), NOT_FOUND(404) } diff --git a/app/src/main/java/com/nextcloud/model/OCFileFilterType.kt b/app/src/main/java/com/nextcloud/model/OCFileFilterType.kt new file mode 100644 index 0000000..81451c2 --- /dev/null +++ b/app/src/main/java/com/nextcloud/model/OCFileFilterType.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.model + +enum class OCFileFilterType { + Shared, + Favorite +} diff --git a/app/src/main/java/com/nextcloud/model/OfflineOperationType.kt b/app/src/main/java/com/nextcloud/model/OfflineOperationType.kt new file mode 100644 index 0000000..7f31258 --- /dev/null +++ b/app/src/main/java/com/nextcloud/model/OfflineOperationType.kt @@ -0,0 +1,32 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.model + +sealed class OfflineOperationType { + abstract val type: String + + data class CreateFolder(override val type: String, var path: String) : OfflineOperationType() + + data class CreateFile( + override val type: String, + val localPath: String, + var remotePath: String, + var mimeType: String + ) : OfflineOperationType() + + data class RenameFile(override val type: String, var ocFileId: Long, val newName: String) : OfflineOperationType() + + data class RemoveFile(override val type: String, var path: String) : OfflineOperationType() +} + +enum class OfflineOperationRawType { + CreateFolder, + CreateFile, + RenameFile, + RemoveFile +} diff --git a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt new file mode 100644 index 0000000..0b36814 --- /dev/null +++ b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt @@ -0,0 +1,28 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.model + +import com.owncloud.android.R + +enum class SearchResultEntryType { + CalendarEvent, + Folder, + Note, + Contact, + Deck, + Unknown; + + fun iconId(): Int = when (this) { + Folder -> R.drawable.folder + Note -> R.drawable.ic_edit + Contact -> R.drawable.file_vcard + CalendarEvent -> R.drawable.file_calendar + Deck -> R.drawable.ic_deck + else -> R.drawable.ic_find_in_page + } +} diff --git a/app/src/main/java/com/nextcloud/model/ShareeEntry.kt b/app/src/main/java/com/nextcloud/model/ShareeEntry.kt new file mode 100644 index 0000000..05e9a6a --- /dev/null +++ b/app/src/main/java/com/nextcloud/model/ShareeEntry.kt @@ -0,0 +1,71 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.model + +import android.content.ContentValues +import com.owncloud.android.db.ProviderMeta.ProviderTableMeta +import com.owncloud.android.lib.resources.files.model.RemoteFile +import com.owncloud.android.lib.resources.shares.ShareType + +data class ShareeEntry( + val filePath: String?, + val accountOwner: String, + val fileOwnerId: String?, + val shareWithDisplayName: String?, + val shareWithUserId: String?, + val shareType: Int +) { + companion object { + /** + * Extracts a list of share-related ContentValues from a given RemoteFile. + * + * Each RemoteFile can be shared with multiple users (sharees), and this function converts each + * sharee into a ContentValues object, representing a row for insertion into a database. + * + * @param remoteFile The RemoteFile object containing sharee information. + * @param accountName The name of the user account that owns this RemoteFile. + * @return A list of ContentValues representing each share entry, or null if no sharees are found. + */ + fun getContentValues(remoteFile: RemoteFile, accountName: String): List? { + if (remoteFile.sharees.isNullOrEmpty()) { + return null + } + + val result = arrayListOf() + + for (share in remoteFile.sharees) { + val shareType: ShareType? = share?.shareType + if (shareType == null) { + continue + } + + val contentValue = ShareeEntry( + remoteFile.remotePath, + accountName, + remoteFile.ownerId, + share.displayName, + share.userId, + shareType.value + ).toContentValues() + + result.add(contentValue) + } + + return result + } + } + + private fun toContentValues(): ContentValues = ContentValues().apply { + put(ProviderTableMeta.OCSHARES_PATH, filePath) + put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, accountOwner) + put(ProviderTableMeta.OCSHARES_USER_ID, fileOwnerId) + put(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME, shareWithDisplayName) + put(ProviderTableMeta.OCSHARES_SHARE_WITH, shareWithUserId) + put(ProviderTableMeta.OCSHARES_SHARE_TYPE, shareType) + } +} diff --git a/app/src/main/java/com/nextcloud/model/ToolbarItem.kt b/app/src/main/java/com/nextcloud/model/ToolbarItem.kt new file mode 100644 index 0000000..8995acc --- /dev/null +++ b/app/src/main/java/com/nextcloud/model/ToolbarItem.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.model + +import android.view.Menu +import com.owncloud.android.R + +enum class ToolbarItem(val navId: Int, val titleId: Int, val style: ToolbarStyle) { + NONE(Menu.NONE, R.string.drawer_item_all_files, ToolbarStyle.SEARCH), + ALL_FILES(R.id.nav_all_files, R.string.drawer_item_all_files, ToolbarStyle.SEARCH), + PERSONAL_FILES(R.id.nav_personal_files, R.string.drawer_item_personal_files, ToolbarStyle.SEARCH), + ACTIVITIES(R.id.nav_activity, R.string.drawer_item_activities, ToolbarStyle.PLAIN), + FAVORITES(R.id.nav_favorites, R.string.drawer_item_favorites, ToolbarStyle.PLAIN), + GALLERY(R.id.nav_gallery, R.string.drawer_item_gallery, ToolbarStyle.PLAIN), + SHARED(R.id.nav_shared, R.string.drawer_item_shared, ToolbarStyle.PLAIN), + GROUP_FOLDERS(R.id.nav_groupfolders, R.string.drawer_item_groupfolders, ToolbarStyle.PLAIN), + ON_DEVICE(R.id.nav_on_device, R.string.drawer_item_on_device, ToolbarStyle.PLAIN), + RECENTLY_MODIFIED(R.id.nav_recently_modified, R.string.drawer_item_recently_modified, ToolbarStyle.PLAIN), + ASSISTANT(R.id.nav_assistant, R.string.drawer_item_assistant, ToolbarStyle.PLAIN), + UPLOADS(R.id.nav_uploads, R.string.drawer_item_uploads_list, ToolbarStyle.PLAIN), + SETTINGS(R.id.nav_settings, R.string.actionbar_settings, ToolbarStyle.PLAIN), + COMMUNITY(R.id.nav_community, R.string.drawer_community, ToolbarStyle.PLAIN), + TRASHBIN(R.id.nav_trashbin, R.string.drawer_item_trashbin, ToolbarStyle.PLAIN); + + companion object { + fun fromNavId(navId: Int): ToolbarItem? = entries.find { it.navId == navId } + } +} + +enum class ToolbarStyle { + PLAIN, + SEARCH +} diff --git a/app/src/main/java/com/nextcloud/model/WorkerState.kt b/app/src/main/java/com/nextcloud/model/WorkerState.kt index d328a96..6a1cca1 100644 --- a/app/src/main/java/com/nextcloud/model/WorkerState.kt +++ b/app/src/main/java/com/nextcloud/model/WorkerState.kt @@ -1,18 +1,20 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.model import com.nextcloud.client.account.User -import com.owncloud.android.db.OCUpload +import com.owncloud.android.datamodel.OCFile import com.owncloud.android.operations.DownloadFileOperation sealed class WorkerState { - object Idle : WorkerState() - class Download(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState() - class Upload(var user: User?, var uploads: List) : WorkerState() + data class DownloadFinished(var currentFile: OCFile?) : WorkerState() + data class DownloadStarted(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState() + data class UploadFinished(var currentFile: OCFile?) : WorkerState() + data class UploadStarted(var user: User?) : WorkerState() + data object OfflineOperationsCompleted : WorkerState() } diff --git a/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt b/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt index d5dfe91..28d92b8 100644 --- a/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt +++ b/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.model @@ -18,10 +18,8 @@ class WorkerStateLiveData private constructor() : LiveData() { companion object { private var instance: WorkerStateLiveData? = null - fun instance(): WorkerStateLiveData { - return instance ?: synchronized(this) { - instance ?: WorkerStateLiveData().also { instance = it } - } + fun instance(): WorkerStateLiveData = instance ?: synchronized(this) { + instance ?: WorkerStateLiveData().also { instance = it } } } } diff --git a/app/src/main/java/com/nextcloud/receiver/NetworkChangeReceiver.kt b/app/src/main/java/com/nextcloud/receiver/NetworkChangeReceiver.kt new file mode 100644 index 0000000..d9d7cbe --- /dev/null +++ b/app/src/main/java/com/nextcloud/receiver/NetworkChangeReceiver.kt @@ -0,0 +1,29 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.receiver + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.nextcloud.client.network.ConnectivityService + +interface NetworkChangeListener { + fun networkAndServerConnectionListener(isNetworkAndServerAvailable: Boolean) +} + +class NetworkChangeReceiver( + private val listener: NetworkChangeListener, + private val connectivityService: ConnectivityService +) : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent?) { + connectivityService.isNetworkAndServerAvailable { + listener.networkAndServerConnectionListener(it) + } + } +} diff --git a/app/src/main/java/com/nextcloud/repository/ClientRepository.kt b/app/src/main/java/com/nextcloud/repository/ClientRepository.kt new file mode 100644 index 0000000..6e42332 --- /dev/null +++ b/app/src/main/java/com/nextcloud/repository/ClientRepository.kt @@ -0,0 +1,45 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.repository + +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.lib.common.OwnCloudClient + +/** + * Interface defining methods to retrieve Nextcloud and OwnCloudClient clients. + * Provides both callback-based and suspend function versions for flexibility in usage. + */ +interface ClientRepository { + /** + * Retrieves an instance of [NextcloudClient] using a callback. + * + * @param onComplete A callback function that receives the [NextcloudClient] instance once available. + */ + fun getNextcloudClient(onComplete: (NextcloudClient) -> Unit) + + /** + * Retrieves an instance of [NextcloudClient] as a suspend function. + * + * @return The [NextcloudClient] instance, or `null` if it cannot be retrieved. + */ + suspend fun getNextcloudClient(): NextcloudClient? + + /** + * Retrieves an instance of [OwnCloudClient] using a callback. + * + * @param onComplete A callback function that receives the [OwnCloudClient] instance once available. + */ + fun getOwncloudClient(onComplete: (OwnCloudClient) -> Unit) + + /** + * Retrieves an instance of [OwnCloudClient] as a suspend function. + * + * @return The [OwnCloudClient] instance, or `null` if it cannot be retrieved. + */ + suspend fun getOwncloudClient(): OwnCloudClient? +} diff --git a/app/src/main/java/com/nextcloud/repository/RemoteClientRepository.kt b/app/src/main/java/com/nextcloud/repository/RemoteClientRepository.kt new file mode 100644 index 0000000..5bdfa82 --- /dev/null +++ b/app/src/main/java/com/nextcloud/repository/RemoteClientRepository.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.repository + +import android.content.Context +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.nextcloud.client.account.User +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory +import com.owncloud.android.lib.common.utils.Log_OC +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +@Suppress("TooGenericExceptionCaught", "DEPRECATION") +class RemoteClientRepository(private val user: User, private val context: Context, lifecycleOwner: LifecycleOwner) : + ClientRepository { + private val tag = "ClientRepository" + private val clientFactory = OwnCloudClientManagerFactory.getDefaultSingleton() + private val scope = lifecycleOwner.lifecycleScope + + override fun getNextcloudClient(onComplete: (NextcloudClient) -> Unit) { + scope.launch(Dispatchers.IO) { + try { + val client = clientFactory.getNextcloudClientFor(user.toOwnCloudAccount(), context) + onComplete(client) + } catch (e: Exception) { + Log_OC.d(tag, "Exception caught getNextcloudClient(): $e") + } + } + } + + override suspend fun getNextcloudClient(): NextcloudClient? = withContext(Dispatchers.IO) { + try { + clientFactory.getNextcloudClientFor(user.toOwnCloudAccount(), context) + } catch (e: Exception) { + Log_OC.d(tag, "Exception caught getNextcloudClient(): $e") + null + } + } + + override fun getOwncloudClient(onComplete: (OwnCloudClient) -> Unit) { + scope.launch(Dispatchers.IO) { + try { + val client = clientFactory.getClientFor(user.toOwnCloudAccount(), context) + onComplete(client) + } catch (e: Exception) { + Log_OC.d(tag, "Exception caught getOwncloudClient(): $e") + } + } + } + + override suspend fun getOwncloudClient(): OwnCloudClient? = withContext(Dispatchers.IO) { + try { + clientFactory.getClientFor(user.toOwnCloudAccount(), context) + } catch (e: Exception) { + Log_OC.d(tag, "Exception caught getOwncloudClient(): $e") + null + } + } +} diff --git a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt index f5b7793..7e63a84 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt @@ -4,9 +4,8 @@ * @author Infomaniak Network SA * Copyright (C) 2020 Infomaniak Network SA * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ - package com.nextcloud.ui import android.annotation.SuppressLint @@ -19,12 +18,14 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.fragment.app.DialogFragment +import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable import com.nextcloud.client.network.ClientFactory import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.R import com.owncloud.android.databinding.DialogChooseAccountBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -34,10 +35,10 @@ import com.owncloud.android.ui.activity.BaseActivity import com.owncloud.android.ui.activity.DrawerActivity import com.owncloud.android.ui.adapter.UserListAdapter import com.owncloud.android.ui.adapter.UserListItem -import com.owncloud.android.ui.asynctasks.RetrieveStatusAsyncTask import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.launch import javax.inject.Inject private const val ARG_CURRENT_USER_PARAM = "currentUser" @@ -55,7 +56,7 @@ class ChooseAccountDialogFragment : private var currentStatus: Status? = null private var _binding: DialogChooseAccountBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! @Inject lateinit var clientFactory: ClientFactory @@ -120,8 +121,7 @@ class ChooseAccountDialogFragment : viewThemeUtils ) - // hide "add account" when no multi account - if (!resources.getBoolean(R.bool.multiaccount_support)) { + if (!MDMConfig.multiAccountSupport(requireContext())) { binding.addAccount.visibility = View.GONE } @@ -138,13 +138,23 @@ class ChooseAccountDialogFragment : (activity as DrawerActivity).openManageAccounts() } - binding.setStatus.setOnClickListener { - val setStatusDialog = SetStatusDialogFragment.newInstance(accountManager.user, currentStatus) + binding.onlineStatus.setOnClickListener { + val setStatusDialog = SetOnlineStatusBottomSheet(currentStatus) setStatusDialog.show((activity as DrawerActivity).supportFragmentManager, "fragment_set_status") dismiss() } + binding.statusMessage.setOnClickListener { + val setStatusMessageDialog = SetStatusMessageBottomSheet(accountManager.user, currentStatus) + setStatusMessageDialog.show( + (activity as DrawerActivity).supportFragmentManager, + "fragment_set_status_message" + ) + + dismiss() + } + val capability = FileDataStorageManager(user, context?.contentResolver) .getCapability(user) @@ -152,18 +162,31 @@ class ChooseAccountDialogFragment : binding.statusView.visibility = View.VISIBLE } - RetrieveStatusAsyncTask(user, this, clientFactory).execute() + loadAndSetUserStatus(user) } themeViews() } + private fun loadAndSetUserStatus(user: User) { + viewLifecycleOwner.lifecycleScope.launch { + val status = retrieveUserStatus(user, clientFactory) + + if (isAdded && !isDetached) { + val context = requireContext() + setStatus(status, context) + } + } + } + private fun themeViews() { viewThemeUtils.platform.themeDialogDivider(binding.separatorLine) viewThemeUtils.platform.themeDialog(binding.root) - viewThemeUtils.material.colorMaterialTextButton(binding.setStatus) - viewThemeUtils.dialog.colorDialogMenuText(binding.setStatus) + viewThemeUtils.material.colorMaterialTextButton(binding.onlineStatus) + viewThemeUtils.dialog.colorDialogMenuText(binding.onlineStatus) + viewThemeUtils.material.colorMaterialTextButton(binding.statusMessage) + viewThemeUtils.dialog.colorDialogMenuText(binding.statusMessage) viewThemeUtils.material.colorMaterialTextButton(binding.addAccount) viewThemeUtils.dialog.colorDialogMenuText(binding.addAccount) viewThemeUtils.material.colorMaterialTextButton(binding.manageAccounts) @@ -187,21 +210,18 @@ class ChooseAccountDialogFragment : */ companion object { @JvmStatic - fun newInstance(user: User) = - ChooseAccountDialogFragment().apply { - arguments = Bundle().apply { - putParcelable(ARG_CURRENT_USER_PARAM, user) - } + fun newInstance(user: User) = ChooseAccountDialogFragment().apply { + arguments = Bundle().apply { + putParcelable(ARG_CURRENT_USER_PARAM, user) } + } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return dialogView - } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + dialogView - override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean { - return (callContext as ImageView).tag.toString() == tag - } + override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean = + (callContext as ImageView).tag.toString() == tag override fun avatarGenerated(avatarDrawable: Drawable?, callContext: Any?) { if (_binding != null) { @@ -227,7 +247,7 @@ class ChooseAccountDialogFragment : binding.currentAccount.status.let { if (newStatus.message.isNullOrBlank()) { - it.text = "" + it.text = getString(R.string.empty) it.visibility = View.GONE } else { it.text = newStatus.message diff --git a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt new file mode 100644 index 0000000..fc9760f --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt @@ -0,0 +1,170 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.ui + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.preference.PreferenceManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.preferences.AppPreferencesImpl +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.databinding.DialogDataStorageLocationBinding +import com.owncloud.android.datastorage.DataStorageProvider +import com.owncloud.android.datastorage.StoragePoint +import com.owncloud.android.datastorage.StoragePoint.PrivacyType +import com.owncloud.android.datastorage.StoragePoint.StorageType +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.theme.ViewThemeUtils +import java.io.File +import javax.inject.Inject + +class ChooseStorageLocationDialogFragment : + DialogFragment(), + Injectable { + + private lateinit var binding: DialogDataStorageLocationBinding + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + private val storagePoints = DataStorageProvider.getInstance().availableStoragePoints + + private val selectedStorageType + get() = if (!binding.storageExternalRadio.isChecked) StorageType.INTERNAL else StorageType.EXTERNAL + private val selectedPrivacyType + get() = if (binding.allowMediaIndexSwitch.isChecked) PrivacyType.PUBLIC else PrivacyType.PRIVATE + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogDataStorageLocationBinding.inflate(layoutInflater) + + viewThemeUtils.material.colorMaterialSwitch(binding.allowMediaIndexSwitch) + viewThemeUtils.platform.themeRadioButton(binding.storageInternalRadio) + viewThemeUtils.platform.themeRadioButton(binding.storageExternalRadio) + + val builder = MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.storage_choose_location) + .setPositiveButton(R.string.common_ok) { dialog: DialogInterface, _ -> + notifyResult() + dialog.dismiss() + }.setView(binding.root) + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder) + + binding.storageRadioGroup.setOnCheckedChangeListener { _, _ -> + updateMediaIndexSwitch() + } + + binding.allowMediaIndexSwitch.setOnCheckedChangeListener { _, _ -> + updateStorageTypeSelection() + } + + return builder.create() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + binding.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setupLocationSelection() + super.onViewCreated(view, savedInstanceState) + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + activity?.finish() + } + + private fun setupLocationSelection() { + updateStorageTypeSelection() + val currentStorageLocation = getCurrentStorageLocation() ?: return + + val radioButton = when (currentStorageLocation.storageType) { + StorageType.EXTERNAL -> binding.storageExternalRadio + else -> binding.storageInternalRadio + } + + radioButton.isChecked = true + updateMediaIndexSwitch() + } + + private fun getStoragePointLabel(storageType: StorageType, privacyType: PrivacyType): String { + val typeString = when (storageType) { + StorageType.INTERNAL -> getString(R.string.storage_internal_storage) + StorageType.EXTERNAL -> getString(R.string.storage_external_storage) + } + + val storagePath = + storagePoints.find { it.storageType == storageType && it.privacyType == privacyType }?.path + + return storagePath?.let { + val file = File(it) + val totalSpace = file.totalSpace + val usedSpace = totalSpace - file.freeSpace + return String.format( + getString(R.string.file_migration_free_space), + typeString, + DisplayUtils.bytesToHumanReadable(usedSpace), + DisplayUtils.bytesToHumanReadable(totalSpace) + ) + } ?: typeString + } + + private fun updateMediaIndexSwitch() { + val privacyTypes = + storagePoints.filter { it.storageType == selectedStorageType }.map { it.privacyType }.distinct() + binding.allowMediaIndexSwitch.isEnabled = privacyTypes.size > 1 + binding.allowMediaIndexSwitch.isChecked = privacyTypes.contains(PrivacyType.PUBLIC) + } + + private fun updateStorageTypeSelection() { + val hasInternalStorage = storagePoints.any { it.storageType == StorageType.INTERNAL } + val hasExternalStorage = storagePoints.any { it.storageType == StorageType.EXTERNAL } + + binding.storageInternalRadio.isEnabled = hasInternalStorage + binding.storageInternalRadio.text = getStoragePointLabel(StorageType.INTERNAL, selectedPrivacyType) + + binding.storageExternalRadio.isEnabled = hasExternalStorage + binding.storageExternalRadio.text = getStoragePointLabel(StorageType.EXTERNAL, selectedPrivacyType) + } + + private fun getCurrentStorageLocation(): StoragePoint? { + val appContext = MainApp.getAppContext() + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext) + val storagePath = sharedPreferences.getString(AppPreferencesImpl.STORAGE_PATH, appContext.filesDir.absolutePath) + return storagePoints.find { it.path == storagePath } + } + + private fun notifyResult() { + val newPath = + storagePoints.find { it.storageType == selectedStorageType && it.privacyType == selectedPrivacyType } + ?: return + + val resultBundle = Bundle().apply { + putString(KEY_RESULT_STORAGE_LOCATION, newPath.path) + } + + parentFragmentManager.setFragmentResult(KEY_RESULT_STORAGE_LOCATION, resultBundle) + } + + companion object { + const val KEY_RESULT_STORAGE_LOCATION = "KEY_RESULT_STORAGE_LOCATION" + const val STORAGE_LOCATION_RESULT_CODE = 100 + + @JvmStatic + fun newInstance() = ChooseStorageLocationDialogFragment() + + @JvmStatic + val TAG: String = Companion::class.java.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/ui/ClearStatusTask.kt b/app/src/main/java/com/nextcloud/ui/ClearStatusTask.kt index 03bd615..0c90f59 100644 --- a/app/src/main/java/com/nextcloud/ui/ClearStatusTask.kt +++ b/app/src/main/java/com/nextcloud/ui/ClearStatusTask.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui @@ -15,15 +15,13 @@ import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.users.ClearStatusMessageRemoteOperation public class ClearStatusTask(val account: Account?, val context: Context?) : Function0 { - override fun invoke(): Boolean { - return try { - val client = OwnCloudClientFactory.createNextcloudClient(account, context) + override fun invoke(): Boolean = try { + val client = OwnCloudClientFactory.createNextcloudClient(account, context) - ClearStatusMessageRemoteOperation().execute(client).isSuccess - } catch (e: AccountUtils.AccountNotFoundException) { - Log_OC.e(this, "Error clearing status", e) + ClearStatusMessageRemoteOperation().execute(client).isSuccess + } catch (e: AccountUtils.AccountNotFoundException) { + Log_OC.e(this, "Error clearing status", e) - false - } + false } } diff --git a/app/src/main/java/com/nextcloud/ui/ImageDetailFragment.kt b/app/src/main/java/com/nextcloud/ui/ImageDetailFragment.kt index 8fc1890..a0c2f73 100644 --- a/app/src/main/java/com/nextcloud/ui/ImageDetailFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ImageDetailFragment.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 ZetaTom * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui @@ -11,7 +11,6 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.graphics.drawable.LayerDrawable -import android.net.Uri import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater @@ -19,12 +18,14 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import androidx.core.content.ContextCompat +import androidx.core.net.toUri import androidx.fragment.app.Fragment import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.NominatimClient import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.extensions.logFileSize import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.databinding.PreviewImageDetailsFragmentBinding @@ -53,7 +54,9 @@ import javax.inject.Inject import kotlin.math.pow import kotlin.math.roundToInt -class ImageDetailFragment : Fragment(), Injectable { +class ImageDetailFragment : + Fragment(), + Injectable { private lateinit var binding: PreviewImageDetailsFragmentBinding private lateinit var file: OCFile private lateinit var user: User @@ -63,6 +66,8 @@ class ImageDetailFragment : Fragment(), Injectable { @Inject lateinit var viewThemeUtils: ViewThemeUtils + private val tag = "ImageDetailFragment" + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = PreviewImageDetailsFragmentBinding.inflate(layoutInflater, container, false) @@ -93,7 +98,8 @@ class ImageDetailFragment : Fragment(), Injectable { } nominatimClient = NominatimClient( - getString(R.string.osm_geocoder_url), getString(R.string.osm_geocoder_contact) + getString(R.string.osm_geocoder_url), + getString(R.string.osm_geocoder_contact) ) return binding.root @@ -101,6 +107,7 @@ class ImageDetailFragment : Fragment(), Injectable { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) + file.logFileSize(tag) outState.putParcelable(ARG_FILE, file) outState.putParcelable(ARG_USER, user) outState.putParcelable(ARG_METADATA, metadata) @@ -323,14 +330,12 @@ class ImageDetailFragment : Fragment(), Injectable { } @SuppressLint("SimpleDateFormat") - private fun formatDate(timestamp: Long): String { - return buildString { - append(SimpleDateFormat("EEEE").format(timestamp)) - append(TEXT_SEP) - append(DateFormat.getDateInstance(DateFormat.MEDIUM).format(timestamp)) - append(TEXT_SEP) - append(DateFormat.getTimeInstance(DateFormat.SHORT).format(timestamp)) - } + private fun formatDate(timestamp: Long): String = buildString { + append(SimpleDateFormat("EEEE").format(timestamp)) + append(TEXT_SEP) + append(DateFormat.getDateInstance(DateFormat.MEDIUM).format(timestamp)) + append(TEXT_SEP) + append(DateFormat.getTimeInstance(DateFormat.SHORT).format(timestamp)) } private fun imagePinDrawable(context: Context): LayerDrawable { @@ -351,14 +356,12 @@ class ImageDetailFragment : Fragment(), Injectable { private fun markerOnGestureListener(latitude: Double, longitude: Double) = object : OnItemGestureListener { override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=$latitude,$longitude")) + val intent = Intent(Intent.ACTION_VIEW, "geo:0,0?q=$latitude,$longitude".toUri()) DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_map_app_availble) return true } - override fun onItemLongPress(index: Int, item: OverlayItem): Boolean { - return false - } + override fun onItemLongPress(index: Int, item: OverlayItem): Boolean = false } @Parcelize @@ -384,12 +387,10 @@ class ImageDetailFragment : Fragment(), Injectable { private const val SCROLL_LIMIT = 80.0 @JvmStatic - fun newInstance(file: OCFile, user: User): ImageDetailFragment { - return ImageDetailFragment().apply { - arguments = Bundle().apply { - putParcelable(ARG_FILE, file) - putParcelable(ARG_USER, user) - } + fun newInstance(file: OCFile, user: User): ImageDetailFragment = ImageDetailFragment().apply { + arguments = Bundle().apply { + putParcelable(ARG_FILE, file) + putParcelable(ARG_USER, user) } } } diff --git a/app/src/main/java/com/nextcloud/ui/RetrieveStatus.kt b/app/src/main/java/com/nextcloud/ui/RetrieveStatus.kt new file mode 100644 index 0000000..8231f07 --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/RetrieveStatus.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Edvard Holst + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.ui + +import com.nextcloud.client.account.User +import com.nextcloud.client.network.ClientFactory +import com.owncloud.android.lib.resources.users.GetStatusRemoteOperation +import com.owncloud.android.lib.resources.users.Status +import com.owncloud.android.lib.resources.users.StatusType +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.IOException + +suspend fun retrieveUserStatus(user: User, clientFactory: ClientFactory): Status = withContext(Dispatchers.IO) { + try { + val client = clientFactory.createNextcloudClient(user) + val result = GetStatusRemoteOperation().execute(client) + if (result.isSuccess && result.resultData is Status) { + result.resultData as Status + } else { + offlineStatus() + } + } catch (e: ClientFactory.CreationException) { + offlineStatus() + } catch (e: IOException) { + offlineStatus() + } +} + +private fun offlineStatus() = Status(StatusType.OFFLINE, "", "", -1) diff --git a/app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt new file mode 100644 index 0000000..f27ef8c --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/SetOnlineStatusBottomSheet.kt @@ -0,0 +1,160 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2020 Nextcloud GmbH + * + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ + +package com.nextcloud.ui + +import android.annotation.SuppressLint +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.google.android.material.card.MaterialCardView +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.core.AsyncRunner +import com.nextcloud.client.di.Injectable +import com.nextcloud.utils.extensions.setVisibleIf +import com.owncloud.android.R +import com.owncloud.android.databinding.SetOnlineStatusBottomSheetBinding +import com.owncloud.android.lib.resources.users.Status +import com.owncloud.android.lib.resources.users.StatusType +import com.owncloud.android.ui.activity.BaseActivity +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.theme.CapabilityUtils +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject + +class SetOnlineStatusBottomSheet(val currentStatus: Status?) : + BottomSheetDialogFragment(R.layout.set_online_status_bottom_sheet), + Injectable { + + private lateinit var binding: SetOnlineStatusBottomSheetBinding + + private lateinit var accountManager: UserAccountManager + + @Inject + lateinit var asyncRunner: AsyncRunner + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + @SuppressLint("DefaultLocale") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + accountManager = (activity as BaseActivity).userAccountManager + + currentStatus?.let { + updateCurrentStatusViews(it) + } + + binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) } + binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) } + binding.busyStatus.setOnClickListener { setStatus(StatusType.BUSY) } + binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) } + binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) } + + viewThemeUtils.files.themeStatusCardView(binding.onlineStatus) + viewThemeUtils.files.themeStatusCardView(binding.awayStatus) + viewThemeUtils.files.themeStatusCardView(binding.busyStatus) + viewThemeUtils.files.themeStatusCardView(binding.dndStatus) + viewThemeUtils.files.themeStatusCardView(binding.invisibleStatus) + + viewThemeUtils.platform.themeDialog(binding.root) + + binding.busyStatus.setVisibleIf(CapabilityUtils.getCapability(context).userStatusSupportsBusy.isTrue) + } + + private fun updateCurrentStatusViews(it: Status) { + visualizeStatus(it.status) + } + + private fun setStatus(statusType: StatusType) { + asyncRunner.postQuickTask( + SetStatusTask( + statusType, + accountManager.currentOwnCloudAccount?.savedAccount, + context + ), + { + if (it) { + dismiss() + } else { + showErrorSnackbar() + } + }, + { + showErrorSnackbar() + } + ) + } + + private fun showErrorSnackbar() { + DisplayUtils.showSnackMessage(view, "Failed to set status!") + clearTopStatus() + } + + private fun visualizeStatus(statusType: StatusType) { + clearTopStatus() + val views: Triple = when (statusType) { + StatusType.ONLINE -> Triple(binding.onlineStatus, binding.onlineHeadline, binding.onlineIcon) + StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon) + StatusType.BUSY -> Triple(binding.busyStatus, binding.busyHeadline, binding.busyIcon) + StatusType.DND -> Triple(binding.dndStatus, binding.dndHeadline, binding.dndIcon) + StatusType.INVISIBLE -> Triple(binding.invisibleStatus, binding.invisibleHeadline, binding.invisibleIcon) + else -> { + Log.d(TAG, "unknown status") + return + } + } + views.first.isChecked = true + viewThemeUtils.platform.colorOnSecondaryContainerTextViewElement(views.second) + } + + private fun clearTopStatus() { + context?.let { + binding.onlineHeadline.setTextColor( + resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) + ) + binding.awayHeadline.setTextColor( + resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) + ) + binding.busyHeadline.setTextColor( + resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) + ) + binding.dndHeadline.setTextColor( + resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) + ) + binding.invisibleHeadline.setTextColor( + resources.getColor(com.nextcloud.android.common.ui.R.color.high_emphasis_text) + ) + + binding.awayIcon.imageTintList = null + binding.dndIcon.imageTintList = null + binding.invisibleIcon.imageTintList = null + + binding.onlineStatus.isChecked = false + binding.awayStatus.isChecked = false + binding.busyStatus.isChecked = false + binding.dndStatus.isChecked = false + binding.invisibleStatus.isChecked = false + } + } + + companion object { + private val TAG = SetOnlineStatusBottomSheet::class.simpleName + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + binding = SetOnlineStatusBottomSheetBinding.inflate(layoutInflater, container, false) + return binding.root + } +} diff --git a/app/src/main/java/com/nextcloud/ui/SetPredefinedCustomStatusTask.kt b/app/src/main/java/com/nextcloud/ui/SetPredefinedCustomStatusTask.kt index 8111280..2cb5b5e 100644 --- a/app/src/main/java/com/nextcloud/ui/SetPredefinedCustomStatusTask.kt +++ b/app/src/main/java/com/nextcloud/ui/SetPredefinedCustomStatusTask.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui @@ -20,15 +20,13 @@ class SetPredefinedCustomStatusTask( val account: Account?, val context: Context? ) : Function0 { - override fun invoke(): Boolean { - return try { - val client = OwnCloudClientFactory.createNextcloudClient(account, context) + override fun invoke(): Boolean = try { + val client = OwnCloudClientFactory.createNextcloudClient(account, context) - SetPredefinedCustomStatusMessageRemoteOperation(messageId, clearAt).execute(client).isSuccess - } catch (e: AccountUtils.AccountNotFoundException) { - Log_OC.e(this, "Error setting predefined status", e) + SetPredefinedCustomStatusMessageRemoteOperation(messageId, clearAt).execute(client).isSuccess + } catch (e: AccountUtils.AccountNotFoundException) { + Log_OC.e(this, "Error setting predefined status", e) - false - } + false } } diff --git a/app/src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/SetStatusMessageBottomSheet.kt similarity index 69% rename from app/src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt rename to app/src/main/java/com/nextcloud/ui/SetStatusMessageBottomSheet.kt index dd12438..1c4f1d1 100644 --- a/app/src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/SetStatusMessageBottomSheet.kt @@ -4,16 +4,14 @@ * @author Tobias Kaminsky * Copyright (C) 2020 Nextcloud GmbH * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui import android.annotation.SuppressLint -import android.app.Dialog import android.content.Context import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -21,28 +19,21 @@ import android.view.inputmethod.InputMethodManager import android.widget.AdapterView import android.widget.AdapterView.OnItemSelectedListener import android.widget.ArrayAdapter -import android.widget.ImageView -import android.widget.TextView import androidx.annotation.VisibleForTesting -import androidx.fragment.app.DialogFragment import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.card.MaterialCardView -import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.core.AsyncRunner import com.nextcloud.client.di.Injectable -import com.nextcloud.client.network.ClientFactory -import com.nextcloud.utils.extensions.getParcelableArgument import com.owncloud.android.R -import com.owncloud.android.databinding.DialogSetStatusBinding +import com.owncloud.android.databinding.SetStatusMessageBottomSheetBinding import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.lib.resources.users.ClearAt import com.owncloud.android.lib.resources.users.PredefinedStatus import com.owncloud.android.lib.resources.users.Status -import com.owncloud.android.lib.resources.users.StatusType import com.owncloud.android.ui.activity.BaseActivity import com.owncloud.android.ui.adapter.PredefinedStatusClickListener import com.owncloud.android.ui.adapter.PredefinedStatusListAdapter @@ -57,9 +48,6 @@ import java.util.Calendar import java.util.Locale import javax.inject.Inject -private const val ARG_CURRENT_USER_PARAM = "currentUser" -private const val ARG_CURRENT_STATUS_PARAM = "currentStatus" - private const val POS_DONT_CLEAR = 0 private const val POS_HALF_AN_HOUR = 1 private const val POS_AN_HOUR = 2 @@ -78,15 +66,13 @@ private const val LAST_SECOND_OF_MINUTE = 59 private const val CLEAR_AT_TYPE_PERIOD = "period" private const val CLEAR_AT_TYPE_END_OF = "end-of" -class SetStatusDialogFragment : - DialogFragment(), +class SetStatusMessageBottomSheet(val user: User, val currentStatus: Status?) : + BottomSheetDialogFragment(R.layout.set_status_message_bottom_sheet), PredefinedStatusClickListener, Injectable { - private lateinit var binding: DialogSetStatusBinding + private lateinit var binding: SetStatusMessageBottomSheetBinding - private var currentUser: User? = null - private var currentStatus: Status? = null private lateinit var accountManager: UserAccountManager private lateinit var predefinedStatus: ArrayList private lateinit var adapter: PredefinedStatusListAdapter @@ -100,40 +86,22 @@ class SetStatusDialogFragment : @Inject lateinit var asyncRunner: AsyncRunner - @Inject - lateinit var clientFactory: ClientFactory - @Inject lateinit var viewThemeUtils: ViewThemeUtils override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - arguments?.let { - currentUser = it.getParcelableArgument(ARG_CURRENT_USER_PARAM, User::class.java) - currentStatus = it.getParcelableArgument(ARG_CURRENT_STATUS_PARAM, Status::class.java) - val json = arbitraryDataProvider.getValue(currentUser, ArbitraryDataProvider.PREDEFINED_STATUS) + val json = arbitraryDataProvider.getValue(user, ArbitraryDataProvider.PREDEFINED_STATUS) - if (json.isNotEmpty()) { - val myType = object : TypeToken>() {}.type - predefinedStatus = Gson().fromJson(json, myType) - } + if (json.isNotEmpty()) { + val myType = object : TypeToken>() {}.type + predefinedStatus = Gson().fromJson(json, myType) } EmojiManager.install(GoogleEmojiProvider()) } - @SuppressLint("InflateParams") - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - binding = DialogSetStatusBinding.inflate(layoutInflater) - - val builder = MaterialAlertDialogBuilder(requireContext()).setView(binding.root) - - viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.statusView.context, builder) - - return builder.create() - } - @SuppressLint("DefaultLocale") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -150,16 +118,6 @@ class SetStatusDialogFragment : binding.predefinedStatusList.adapter = adapter binding.predefinedStatusList.layoutManager = LinearLayoutManager(context) - binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) } - binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) } - binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) } - binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) } - - viewThemeUtils.files.themeStatusCardView(binding.onlineStatus) - viewThemeUtils.files.themeStatusCardView(binding.dndStatus) - viewThemeUtils.files.themeStatusCardView(binding.awayStatus) - viewThemeUtils.files.themeStatusCardView(binding.invisibleStatus) - binding.clearStatus.setOnClickListener { clearStatus() } binding.setStatus.setOnClickListener { setStatusMessage() } binding.emoji.setOnClickListener { popup.show() } @@ -204,15 +162,19 @@ class SetStatusDialogFragment : } private fun updateCurrentStatusViews(it: Status) { - binding.emoji.setText(it.icon) + if (it.icon.isNullOrBlank()) { + binding.emoji.setText("😀") + } else { + binding.emoji.setText(it.icon) + } + binding.customStatusInput.text?.clear() binding.customStatusInput.setText(it.message) - visualizeStatus(it.status) if (it.clearAt > 0) { binding.clearStatusAfterSpinner.visibility = View.GONE binding.remainingClearTime.apply { - binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message) + binding.clearStatusMessageTextView.text = getString(R.string.clear) visibility = View.VISIBLE text = DisplayUtils.getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true) .toString() @@ -220,7 +182,7 @@ class SetStatusDialogFragment : setOnClickListener { visibility = View.GONE binding.clearStatusAfterSpinner.visibility = View.VISIBLE - binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) + binding.clearStatusMessageTextView.text = getString(R.string.clear_status_after) } } } @@ -233,20 +195,24 @@ class SetStatusDialogFragment : // 30 minutes System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + THIRTY_MINUTES * ONE_MINUTE_IN_SECONDS } + POS_AN_HOUR -> { // one hour System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS } + POS_FOUR_HOURS -> { // four hours System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + FOUR_HOURS * ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS } + POS_TODAY -> { // today val date = getLastSecondOfToday() dateToSeconds(date) } + POS_END_OF_WEEK -> { // end of week val date = getLastSecondOfToday() @@ -255,6 +221,7 @@ class SetStatusDialogFragment : } dateToSeconds(date) } + else -> clearAt } } @@ -263,10 +230,12 @@ class SetStatusDialogFragment : clearAt?.type == CLEAR_AT_TYPE_PERIOD -> { System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + clearAt.time.toLong() } + clearAt?.type == CLEAR_AT_TYPE_END_OF && clearAt.time == "day" -> { val date = getLastSecondOfToday() dateToSeconds(date) } + else -> -1 } @@ -288,59 +257,6 @@ class SetStatusDialogFragment : ) } - private fun setStatus(statusType: StatusType) { - visualizeStatus(statusType) - - asyncRunner.postQuickTask( - SetStatusTask( - statusType, - accountManager.currentOwnCloudAccount?.savedAccount, - context - ), - { - if (!it) { - clearTopStatus() - } - }, - { clearTopStatus() } - ) - } - - private fun visualizeStatus(statusType: StatusType) { - clearTopStatus() - val views: Triple = when (statusType) { - StatusType.ONLINE -> Triple(binding.onlineStatus, binding.onlineHeadline, binding.onlineIcon) - StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon) - StatusType.DND -> Triple(binding.dndStatus, binding.dndHeadline, binding.dndIcon) - StatusType.INVISIBLE -> Triple(binding.invisibleStatus, binding.invisibleHeadline, binding.invisibleIcon) - else -> { - Log.d(TAG, "unknown status") - return - } - } - views.first.isChecked = true - viewThemeUtils.platform.colorOnSecondaryContainerTextViewElement(views.second) - } - - private fun clearTopStatus() { - context?.let { - binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) - binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) - binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) - binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) - - binding.onlineIcon.imageTintList = null - binding.awayIcon.imageTintList = null - binding.dndIcon.imageTintList = null - binding.invisibleIcon.imageTintList = null - - binding.onlineStatus.isChecked = false - binding.awayStatus.isChecked = false - binding.dndStatus.isChecked = false - binding.invisibleStatus.isChecked = false - } - } - private fun setStatusMessage() { if (selectedPredefinedMessageId != null) { asyncRunner.postQuickTask( @@ -369,27 +285,13 @@ class SetStatusDialogFragment : private fun dismiss(boolean: Boolean) { if (boolean) { dismiss() - } - } - - /** - * Fragment creator - */ - companion object { - private val TAG = SetStatusDialogFragment::class.simpleName - - @JvmStatic - fun newInstance(user: User, status: Status?): SetStatusDialogFragment { - val args = Bundle() - args.putParcelable(ARG_CURRENT_USER_PARAM, user) - args.putParcelable(ARG_CURRENT_STATUS_PARAM, status) - val dialogFragment = SetStatusDialogFragment() - dialogFragment.arguments = args - return dialogFragment + } else { + DisplayUtils.showSnackMessage(view, view?.resources?.getString(R.string.error_setting_status_message)) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + binding = SetStatusMessageBottomSheetBinding.inflate(layoutInflater, container, false) return binding.root } @@ -402,7 +304,7 @@ class SetStatusDialogFragment : binding.remainingClearTime.visibility = View.GONE binding.clearStatusAfterSpinner.visibility = View.VISIBLE - binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after) + binding.clearStatusMessageTextView.text = getString(R.string.clear_status_after) val clearAt = predefinedStatus.clearAt if (clearAt == null) { diff --git a/app/src/main/java/com/nextcloud/ui/SetStatusTask.kt b/app/src/main/java/com/nextcloud/ui/SetStatusTask.kt index a7a2ae9..5477190 100644 --- a/app/src/main/java/com/nextcloud/ui/SetStatusTask.kt +++ b/app/src/main/java/com/nextcloud/ui/SetStatusTask.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui @@ -15,20 +15,14 @@ import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.users.SetStatusRemoteOperation import com.owncloud.android.lib.resources.users.StatusType -class SetStatusTask( - val statusType: StatusType, - val account: Account?, - val context: Context? -) : Function0 { - override fun invoke(): Boolean { - return try { - val client = OwnCloudClientFactory.createNextcloudClient(account, context) +class SetStatusTask(val statusType: StatusType, val account: Account?, val context: Context?) : Function0 { + override fun invoke(): Boolean = try { + val client = OwnCloudClientFactory.createNextcloudClient(account, context) - SetStatusRemoteOperation(statusType).execute(client).isSuccess - } catch (e: AccountUtils.AccountNotFoundException) { - Log_OC.e(this, "Error setting status", e) + SetStatusRemoteOperation(statusType).execute(client).isSuccess + } catch (e: AccountUtils.AccountNotFoundException) { + Log_OC.e(this, "Error setting status", e) - false - } + false } } diff --git a/app/src/main/java/com/nextcloud/ui/SetUserDefinedCustomStatusTask.kt b/app/src/main/java/com/nextcloud/ui/SetUserDefinedCustomStatusTask.kt index f4cf104..a35f01c 100644 --- a/app/src/main/java/com/nextcloud/ui/SetUserDefinedCustomStatusTask.kt +++ b/app/src/main/java/com/nextcloud/ui/SetUserDefinedCustomStatusTask.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui diff --git a/app/src/main/java/com/nextcloud/ui/SquareLoaderImageView.kt b/app/src/main/java/com/nextcloud/ui/SquareLoaderImageView.kt index d7bbccc..4395748 100644 --- a/app/src/main/java/com/nextcloud/ui/SquareLoaderImageView.kt +++ b/app/src/main/java/com/nextcloud/ui/SquareLoaderImageView.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui diff --git a/app/src/main/java/com/nextcloud/ui/behavior/OnScrollBehavior.kt b/app/src/main/java/com/nextcloud/ui/behavior/OnScrollBehavior.kt new file mode 100644 index 0000000..96dedbb --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/behavior/OnScrollBehavior.kt @@ -0,0 +1,36 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.ui.behavior + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.google.android.material.behavior.HideViewOnScrollBehavior + +class OnScrollBehavior @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + HideViewOnScrollBehavior(context, attrs) { + + override fun onNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: V, + target: View, + dxConsumed: Int, + dyConsumed: Int, + dxUnconsumed: Int, + dyUnconsumed: Int, + type: Int, + consumed: IntArray + ) { + if (dyConsumed > 0) { + slideOut(child) + } else if (dyConsumed < 0 || dyUnconsumed < 0) { + slideIn(child) + } + } +} diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index f0de1e0..125f700 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -1,13 +1,12 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.composeActivity -import android.content.Context import android.os.Bundle import android.view.MenuItem import androidx.compose.material3.MaterialTheme @@ -21,27 +20,18 @@ import com.nextcloud.client.assistant.AssistantScreen import com.nextcloud.client.assistant.AssistantViewModel import com.nextcloud.client.assistant.repository.AssistantRepository import com.nextcloud.common.NextcloudClient -import com.nextcloud.common.User import com.nextcloud.utils.extensions.getSerializableArgument import com.owncloud.android.R import com.owncloud.android.databinding.ActivityComposeBinding -import com.owncloud.android.lib.common.OwnCloudClientFactory -import com.owncloud.android.lib.common.accounts.AccountUtils -import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.activity.DrawerActivity -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.lang.ref.WeakReference class ComposeActivity : DrawerActivity() { lateinit var binding: ActivityComposeBinding - private var menuItemId: Int = R.id.nav_all_files companion object { const val DESTINATION = "DESTINATION" const val TITLE = "TITLE" - const val MENU_ITEM = "MENU_ITEM" } override fun onCreate(savedInstanceState: Bundle?) { @@ -51,9 +41,8 @@ class ComposeActivity : DrawerActivity() { val destination = intent.getSerializableArgument(DESTINATION, ComposeDestination::class.java) val titleId = intent.getIntExtra(TITLE, R.string.empty) - menuItemId = intent.getIntExtra(MENU_ITEM, R.id.nav_all_files) - setupDrawer(menuItemId) + setupDrawer() setupToolbarShowOnlyMenuButtonAndTitle(getString(titleId)) { openDrawer() @@ -63,56 +52,42 @@ class ComposeActivity : DrawerActivity() { MaterialTheme( colorScheme = viewThemeUtils.getColorScheme(this), content = { - Content(destination, storageManager.user, this) + Content(destination) } ) } } - override fun onResume() { - super.onResume() - setDrawerMenuItemChecked(menuItemId) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - toggleDrawer() - true - } - else -> super.onOptionsItemSelected(item) + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + android.R.id.home -> { + toggleDrawer() + true } + else -> super.onOptionsItemSelected(item) } @Composable - private fun Content(destination: ComposeDestination?, user: User, context: Context) { + private fun Content(destination: ComposeDestination?) { var nextcloudClient by remember { mutableStateOf(null) } LaunchedEffect(Unit) { - nextcloudClient = getNextcloudClient(user, context) + nextcloudClient = clientRepository.getNextcloudClient() } if (destination == ComposeDestination.AssistantScreen) { + binding.bottomNavigation.menu.findItem(R.id.nav_assistant).run { + isChecked = true + } + nextcloudClient?.let { client -> AssistantScreen( viewModel = AssistantViewModel( - repository = AssistantRepository(client), - context = WeakReference(this) + repository = AssistantRepository(client, capabilities) ), - activity = this + activity = this, + capability = capabilities ) } } } - - private suspend fun getNextcloudClient(user: User, context: Context): NextcloudClient? { - return withContext(Dispatchers.IO) { - try { - OwnCloudClientFactory.createNextcloudClient(user, context) - } catch (e: AccountUtils.AccountNotFoundException) { - Log_OC.e(this, "Error caught at init of createNextcloudClient", e) - null - } - } - } } diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt index e442a5c..10e80ad 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.composeActivity diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt index 042f1c4..bcf724e 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.composeComponents.alertDialog @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.AlertDialog import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -41,6 +42,10 @@ fun SimpleAlertDialog( } AlertDialog( + containerColor = MaterialTheme.colorScheme.surface, + iconContentColor = MaterialTheme.colorScheme.onPrimaryContainer, + titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, + textContentColor = MaterialTheme.colorScheme.onPrimaryContainer, onDismissRequest = { dismiss() }, title = { Text(text = title) diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt index ae5bb94..b3d409a 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt @@ -1,8 +1,8 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.composeComponents.bottomSheet @@ -37,16 +37,13 @@ import kotlinx.coroutines.launch @SuppressLint("ResourceAsColor") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MoreActionsBottomSheet( - title: String? = null, - actions: List Unit>>, - dismiss: () -> Unit -) { +fun MoreActionsBottomSheet(title: String? = null, actions: List Unit>>, dismiss: () -> Unit) { val sheetState = rememberModalBottomSheetState() val scope = rememberCoroutineScope() ModalBottomSheet( modifier = Modifier.padding(top = 32.dp), + containerColor = colorScheme.surface, onDismissRequest = { dismiss() }, @@ -76,8 +73,8 @@ fun MoreActionsBottomSheet( .launch { sheetState.hide() } .invokeOnCompletion { if (!sheetState.isVisible) { - action.third() dismiss() + action.third() } } } diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt index 6f20319..f66521e 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt @@ -1,9 +1,10 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.fileactions @@ -11,8 +12,13 @@ import androidx.annotation.DrawableRes import androidx.annotation.IdRes import androidx.annotation.StringRes import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile -enum class FileAction(@IdRes val id: Int, @StringRes val title: Int, @DrawableRes val icon: Int? = null) { +enum class FileAction( + @param:IdRes val id: Int, + @param:StringRes val title: Int, + @param:DrawableRes val icon: Int? = null +) { // selection SELECT_ALL(R.id.action_select_all_action_menu, R.string.select_all, R.drawable.ic_select_all), SELECT_NONE(R.id.action_deselect_all_action_menu, R.string.deselect_all, R.drawable.ic_select_none), @@ -21,14 +27,15 @@ enum class FileAction(@IdRes val id: Int, @StringRes val title: Int, @DrawableRe EDIT(R.id.action_edit, R.string.action_edit, R.drawable.ic_edit), SEE_DETAILS(R.id.action_see_details, R.string.actionbar_see_details, R.drawable.ic_information_outline), REMOVE_FILE(R.id.action_remove_file, R.string.common_remove, R.drawable.ic_delete), + LEAVE_SHARE(R.id.action_remove_file, R.string.common_leave_this_share, R.drawable.ic_cancel), // File moving RENAME_FILE(R.id.action_rename_file, R.string.common_rename, R.drawable.ic_rename), MOVE_OR_COPY(R.id.action_move_or_copy, R.string.actionbar_move_or_copy, R.drawable.ic_external), // favorites - FAVORITE(R.id.action_favorite, R.string.favorite, R.drawable.ic_star), - UNSET_FAVORITE(R.id.action_unset_favorite, R.string.unset_favorite, R.drawable.ic_star_outline), + FAVORITE(R.id.action_favorite, R.string.favorite, R.drawable.ic_star_outline), + UNSET_FAVORITE(R.id.action_unset_favorite, R.string.unset_favorite, R.drawable.ic_star), // Uploads and downloads DOWNLOAD_FILE(R.id.action_download_file, R.string.filedetails_download, R.drawable.ic_cloud_download), @@ -52,37 +59,178 @@ enum class FileAction(@IdRes val id: Int, @StringRes val title: Int, @DrawableRe LOCK_FILE(R.id.action_lock_file, R.string.lock_file, R.drawable.ic_lock), // Shortcuts - PIN_TO_HOMESCREEN(R.id.action_pin_to_homescreen, R.string.pin_home, R.drawable.add_to_home_screen); + PIN_TO_HOMESCREEN(R.id.action_pin_to_homescreen, R.string.pin_home, R.drawable.add_to_home_screen), + + // Retry for offline operation + RETRY(R.id.action_retry, R.string.retry, R.drawable.ic_retry); companion object { /** * All file actions, in the order they should be displayed */ - @JvmField - val SORTED_VALUES = listOf( - UNLOCK_FILE, - EDIT, - FAVORITE, - UNSET_FAVORITE, - SEE_DETAILS, - LOCK_FILE, - RENAME_FILE, - MOVE_OR_COPY, - DOWNLOAD_FILE, - EXPORT_FILE, - STREAM_MEDIA, - SEND_SHARE_FILE, - SEND_FILE, - OPEN_FILE_WITH, - SYNC_FILE, - CANCEL_SYNC, - SELECT_ALL, - SELECT_NONE, - SET_ENCRYPTED, - UNSET_ENCRYPTED, - SET_AS_WALLPAPER, - REMOVE_FILE, - PIN_TO_HOMESCREEN - ) + fun getActions(files: Collection): List { + return mutableListOf( + UNLOCK_FILE, + EDIT, + FAVORITE, + UNSET_FAVORITE, + SEE_DETAILS, + LOCK_FILE, + RENAME_FILE, + MOVE_OR_COPY, + DOWNLOAD_FILE, + EXPORT_FILE, + STREAM_MEDIA, + SEND_SHARE_FILE, + SEND_FILE, + OPEN_FILE_WITH, + SYNC_FILE, + CANCEL_SYNC, + SELECT_ALL, + SELECT_NONE, + SET_ENCRYPTED, + UNSET_ENCRYPTED, + SET_AS_WALLPAPER, + PIN_TO_HOMESCREEN, + RETRY + ).apply { + val deleteOrLeaveShareAction = getDeleteOrLeaveShareAction(files) ?: return@apply + add(deleteOrLeaveShareAction) + } + } + + fun getFilePreviewActions(file: OCFile?): List { + val result = mutableSetOf( + R.id.action_rename_file, + R.id.action_sync_file, + R.id.action_move_or_copy, + R.id.action_favorite, + R.id.action_unset_favorite, + R.id.action_pin_to_homescreen + ) + + if (file != null) { + val actionsToHide = getActionsToHide(setOf(file)) + result.removeAll(actionsToHide) + } + + return result.toList() + } + + fun getFileDetailActions(file: OCFile?): List { + val result = mutableSetOf( + R.id.action_lock_file, + R.id.action_unlock_file, + R.id.action_edit, + R.id.action_favorite, + R.id.action_unset_favorite, + R.id.action_see_details, + R.id.action_move_or_copy, + R.id.action_stream_media, + R.id.action_send_share_file, + R.id.action_pin_to_homescreen + ) + + if (file?.isFolder == true) { + result.add(R.id.action_send_file) + result.add(R.id.action_sync_file) + } + + if (file?.isAPKorAAB == true) { + result.add(R.id.action_download_file) + result.add(R.id.action_export_file) + } + + if (file != null) { + val actionsToHide = getActionsToHide(setOf(file)) + result.removeAll(actionsToHide) + } + + return result.toList() + } + + fun getFileListActionsToHide(checkedFiles: Set): List { + val result = mutableSetOf() + + if (checkedFiles.any { it.isOfflineOperation }) { + result.addAll( + listOf( + R.id.action_favorite, + R.id.action_move_or_copy, + R.id.action_sync_file, + R.id.action_encrypted, + R.id.action_unset_encrypted, + R.id.action_edit, + R.id.action_download_file, + R.id.action_export_file, + R.id.action_set_as_wallpaper + ) + ) + } + + if (checkedFiles.any { it.isAPKorAAB }) { + result.addAll( + listOf( + R.id.action_send_share_file, + R.id.action_export_file, + R.id.action_sync_file, + R.id.action_download_file + ) + ) + } + + val actionsToHide = getActionsToHide(checkedFiles) + result.addAll(actionsToHide) + + return result.toList() + } + + fun getActionsToHide(files: Set): List { + if (files.isEmpty()) return emptyList() + + val result = mutableListOf() + + if (files.any { !it.canReshare() }) { + result.add(R.id.action_send_share_file) + } + + if (files.any { !it.canRename() }) { + result.add(R.id.action_rename_file) + } + + if (files.any { !it.canMove() }) { + result.add(R.id.action_move_or_copy) + } + + if (files.any { !it.canWrite() }) { + result.add(R.id.action_edit) + } + + if (files.any { it.isRecommendedFile }) { + val allowedForRecommended = setOf( + R.id.action_see_details, + R.id.action_set_as_wallpaper, + R.id.action_pin_to_homescreen, + R.id.action_open_file_with + ) + + val allActions = entries.map { it.id } + result.addAll(allActions - allowedForRecommended) + } + + return result + } + + private fun getDeleteOrLeaveShareAction(files: Collection): FileAction? { + if (files.any { !it.canDeleteOrLeaveShare() }) { + return null + } + + return if (files.any { it.isSharedWithMe }) { + LEAVE_SHARE + } else { + REMOVE_FILE + } + } } } diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt index 92acbdf..63ccd3b 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.fileactions @@ -33,6 +33,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.di.Injectable import com.nextcloud.client.di.ViewModelFactory +import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R import com.owncloud.android.databinding.FileActionsBottomSheetBinding import com.owncloud.android.databinding.FileActionsBottomSheetItemBinding @@ -44,10 +45,13 @@ import com.owncloud.android.lib.resources.files.model.FileLockType import com.owncloud.android.ui.activity.ComponentsGetter import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener +import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject -class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { +class FileActionsBottomSheet : + BottomSheetDialogFragment(), + Injectable { @Inject lateinit var viewThemeUtils: ViewThemeUtils @@ -67,14 +71,14 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { private lateinit var viewModel: FileActionsViewModel private var _binding: FileActionsBottomSheetBinding? = null - private val binding + val binding get() = _binding!! private lateinit var componentsGetter: ComponentsGetter private val thumbnailAsyncTasks = mutableListOf() - interface ResultListener { + fun interface ResultListener { fun onResult(@IdRes actionId: Int) } @@ -99,9 +103,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { return binding.root } - private fun handleState( - state: FileActionsViewModel.UiState - ) { + private fun handleState(state: FileActionsViewModel.UiState) { toggleLoadingOrContent(state) when (state) { is FileActionsViewModel.UiState.LoadedForSingleFile -> { @@ -192,9 +194,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { } } - private fun displayActions( - actions: List - ) { + private fun displayActions(actions: List) { if (binding.fileActionsList.isEmpty()) { actions.forEach { action -> val view = inflateActionView(action) @@ -206,11 +206,23 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { private fun displayTitle(titleFile: OCFile?) { val decryptedFileName = titleFile?.decryptedFileName if (decryptedFileName != null) { - decryptedFileName.let { - binding.title.text = it + val isFolder = titleFile.isFolder + val isRTL = DisplayUtils.isRTL() + val (base, ext) = FileStorageUtils.getFilenameAndExtension(decryptedFileName, isFolder, isRTL) + val titleMaxWidth = DisplayUtils.convertDpToPixel( + requireContext().resources.configuration.screenWidthDp.times(FILENAME_MAX_WIDTH_PERCENTAGE).toFloat(), + context + ) + + binding.title.maxWidth = titleMaxWidth + binding.title.text = base + binding.extension.setVisibleIf(!isFolder) + if (!isFolder) { + binding.extension.text = ext } } else { binding.title.isVisible = false + binding.extension.isVisible = false } } @@ -238,9 +250,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { icon.setImageDrawable(avatarDrawable) } - override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean { - return false - } + override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean = false } DisplayUtils.setAvatar( currentUserProvider.user, @@ -304,6 +314,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { companion object { private const val REQUEST_KEY = "REQUEST_KEY_ACTION" private const val RESULT_KEY_ACTION_ID = "RESULT_KEY_ACTION_ID" + private const val FILENAME_MAX_WIDTH_PERCENTAGE = 0.6 @JvmStatic @JvmOverloads @@ -312,9 +323,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { isOverflow: Boolean, @IdRes additionalToHide: List? = null - ): FileActionsBottomSheet { - return newInstance(1, listOf(file), isOverflow, additionalToHide, true) - } + ): FileActionsBottomSheet = newInstance(1, listOf(file), isOverflow, additionalToHide, true) @JvmStatic @JvmOverloads @@ -325,19 +334,17 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { @IdRes additionalToHide: List? = null, inSingleFileFragment: Boolean = false - ): FileActionsBottomSheet { - return FileActionsBottomSheet().apply { - val argsBundle = bundleOf( - FileActionsViewModel.ARG_ALL_FILES_COUNT to numberOfAllFiles, - FileActionsViewModel.ARG_FILES to ArrayList(files), - FileActionsViewModel.ARG_IS_OVERFLOW to isOverflow, - FileActionsViewModel.ARG_IN_SINGLE_FILE_FRAGMENT to inSingleFileFragment - ) - additionalToHide?.let { - argsBundle.putIntArray(FileActionsViewModel.ARG_ADDITIONAL_FILTER, additionalToHide.toIntArray()) - } - arguments = argsBundle + ): FileActionsBottomSheet = FileActionsBottomSheet().apply { + val argsBundle = bundleOf( + FileActionsViewModel.ARG_ALL_FILES_COUNT to numberOfAllFiles, + FileActionsViewModel.ARG_FILES to ArrayList(files), + FileActionsViewModel.ARG_IS_OVERFLOW to isOverflow, + FileActionsViewModel.ARG_IN_SINGLE_FILE_FRAGMENT to inSingleFileFragment + ) + additionalToHide?.let { + argsBundle.putIntArray(FileActionsViewModel.ARG_ADDITIONAL_FILTER, additionalToHide.toIntArray()) } + arguments = argsBundle } } } diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt index faf0c22..f42015b 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.fileactions @@ -28,8 +28,7 @@ class FileActionsViewModel @Inject constructor( private val currentAccountProvider: CurrentAccountProvider, private val filterFactory: FileMenuFilter.Factory, private val logger: Logger -) : - ViewModel() { +) : ViewModel() { data class LockInfo(val lockType: FileLockType, val lockedBy: String, val lockedUntil: Long?) @@ -54,10 +53,7 @@ class FileActionsViewModel @Inject constructor( @IdRes get() = _clickActionId - fun load( - arguments: Bundle, - componentsGetter: ComponentsGetter - ) { + fun load(arguments: Bundle, componentsGetter: ComponentsGetter) { val files: List? = arguments.getParcelableArrayList(ARG_FILES) val numberOfAllFiles: Int = arguments.getInt(ARG_ALL_FILES_COUNT, 1) val isOverflow = arguments.getBoolean(ARG_IS_OVERFLOW, false) @@ -82,7 +78,7 @@ class FileActionsViewModel @Inject constructor( ) { viewModelScope.launch(Dispatchers.IO) { val toHide = getHiddenActions(componentsGetter, numberOfAllFiles, files, isOverflow, inSingleFileFragment) - val availableActions = getActionsToShow(additionalFilter, toHide) + val availableActions = getActionsToShow(additionalFilter, toHide, files) updateStateLoaded(files, availableActions) } } @@ -93,28 +89,21 @@ class FileActionsViewModel @Inject constructor( files: Collection, isOverflow: Boolean?, inSingleFileFragment: Boolean - ): List { - return filterFactory.newInstance( - numberOfAllFiles ?: 1, - files.toList(), - componentsGetter, - isOverflow ?: false, - currentAccountProvider.user - ) - .getToHide(inSingleFileFragment) - } + ): List = filterFactory.newInstance( + numberOfAllFiles ?: 1, + files.toList(), + componentsGetter, + isOverflow ?: false, + currentAccountProvider.user + ) + .getToHide(inSingleFileFragment) - private fun getActionsToShow( - additionalFilter: IntArray?, - toHide: List - ) = FileAction.SORTED_VALUES - .filter { additionalFilter == null || it.id !in additionalFilter } - .filter { it.id !in toHide } + private fun getActionsToShow(additionalFilter: IntArray?, toHide: List, files: Collection) = + FileAction.getActions(files) + .filter { additionalFilter == null || it.id !in additionalFilter } + .filter { it.id !in toHide } - private fun updateStateLoaded( - files: Collection, - availableActions: List - ) { + private fun updateStateLoaded(files: Collection, availableActions: List) { val state: UiState = when (files.size) { 1 -> { val file = files.first() @@ -135,12 +124,10 @@ class FileActionsViewModel @Inject constructor( } } - private fun getLockedUntil(file: OCFile): Long? { - return if (file.lockTimestamp == 0L || file.lockTimeout == 0L) { - null - } else { - (file.lockTimestamp + file.lockTimeout) * TimeConstants.MILLIS_PER_SECOND - } + private fun getLockedUntil(file: OCFile): Long? = if (file.lockTimestamp == 0L || file.lockTimeout == 0L) { + null + } else { + (file.lockTimestamp + file.lockTimeout) * TimeConstants.MILLIS_PER_SECOND } fun onClick(action: FileAction) { diff --git a/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileAction.kt b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileAction.kt new file mode 100644 index 0000000..9054e8f --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileAction.kt @@ -0,0 +1,32 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.nextcloud.ui.trashbinFileActions + +import androidx.annotation.DrawableRes +import androidx.annotation.IdRes +import androidx.annotation.StringRes +import com.owncloud.android.R + +enum class TrashbinFileAction(@IdRes val id: Int, @StringRes val title: Int, @DrawableRes val icon: Int? = null) { + DELETE_PERMANENTLY(R.id.action_delete, R.string.trashbin_file_remove, R.drawable.ic_delete), + RESTORE(R.id.restore, R.string.restore_item, R.drawable.ic_history), + SELECT_ALL(R.id.action_select_all_action_menu, R.string.select_all, R.drawable.ic_select_all), + SELECT_NONE(R.id.action_deselect_all_action_menu, R.string.deselect_all, R.drawable.ic_select_none); + + companion object { + /** + * All file actions, in the order they should be displayed + */ + @JvmField + val SORTED_VALUES = listOf( + DELETE_PERMANENTLY, + RESTORE, + SELECT_ALL, + SELECT_NONE + ) + } +} diff --git a/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt new file mode 100644 index 0000000..b97c9ba --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsBottomSheet.kt @@ -0,0 +1,234 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.nextcloud.ui.trashbinFileActions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.annotation.IdRes +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.os.bundleOf +import androidx.core.view.isEmpty +import androidx.core.view.isVisible +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.setFragmentResult +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModelProvider +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.nextcloud.client.account.CurrentAccountProvider +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.di.ViewModelFactory +import com.nextcloud.utils.extensions.toOCFile +import com.owncloud.android.R +import com.owncloud.android.databinding.FileActionsBottomSheetBinding +import com.owncloud.android.databinding.FileActionsBottomSheetItemBinding +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.SyncedFolderProvider +import com.owncloud.android.datamodel.ThumbnailsCacheManager +import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject + +class TrashbinFileActionsBottomSheet : + BottomSheetDialogFragment(), + Injectable { + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + @Inject + lateinit var vmFactory: ViewModelFactory + + @Inject + lateinit var currentUserProvider: CurrentAccountProvider + + @Inject + lateinit var storageManager: FileDataStorageManager + + @Inject + lateinit var syncedFolderProvider: SyncedFolderProvider + + private lateinit var viewModel: TrashbinFileActionsViewModel + + private var _binding: FileActionsBottomSheetBinding? = null + val binding + get() = _binding!! + + private val thumbnailAsyncTasks = mutableListOf() + + fun interface ResultListener { + fun onResult(@IdRes actionId: Int) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + viewModel = ViewModelProvider(this, vmFactory)[TrashbinFileActionsViewModel::class.java] + _binding = FileActionsBottomSheetBinding.inflate(inflater, container, false) + + viewModel.uiState.observe(viewLifecycleOwner, this::handleState) + + viewModel.clickActionId.observe(viewLifecycleOwner) { id -> + dispatchActionClick(id) + } + + viewModel.load(requireArguments()) + + val bottomSheetDialog = dialog as BottomSheetDialog + bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED + bottomSheetDialog.behavior.skipCollapsed = true + + viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) + + return binding.root + } + + private fun handleState(state: TrashbinFileActionsViewModel.UiState) { + toggleLoadingOrContent(state) + when (state) { + is TrashbinFileActionsViewModel.UiState.LoadedForSingleFile -> { + loadFileThumbnail(state.titleFile) + displayActions(state.actions) + displayTitle(state.titleFile) + } + + is TrashbinFileActionsViewModel.UiState.LoadedForMultipleFiles -> { + setMultipleFilesThumbnail() + displayActions(state.actions) + displayTitle(state.fileCount) + } + + TrashbinFileActionsViewModel.UiState.Loading -> {} + TrashbinFileActionsViewModel.UiState.Error -> { + context?.let { + Toast.makeText(it, R.string.error_file_actions, Toast.LENGTH_SHORT).show() + } + dismissAllowingStateLoss() + } + } + } + + private fun loadFileThumbnail(titleFile: TrashbinFile?) { + titleFile?.let { + DisplayUtils.setThumbnail( + it.toOCFile(), + binding.thumbnailLayout.thumbnail, + currentUserProvider.user, + storageManager, + thumbnailAsyncTasks, + false, + context, + binding.thumbnailLayout.thumbnailShimmer, + syncedFolderProvider.preferences, + viewThemeUtils, + syncedFolderProvider + ) + } + } + + private fun setMultipleFilesThumbnail() { + context?.let { + val drawable = viewThemeUtils.platform.tintDrawable(it, R.drawable.file_multiple, ColorRole.PRIMARY) + binding.thumbnailLayout.thumbnail.setImageDrawable(drawable) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + fun setResultListener( + fragmentManager: FragmentManager, + lifecycleOwner: LifecycleOwner, + listener: ResultListener + ): TrashbinFileActionsBottomSheet { + fragmentManager.setFragmentResultListener(REQUEST_KEY, lifecycleOwner) { _, result -> + @IdRes val actionId = result.getInt(RESULT_KEY_ACTION_ID, -1) + if (actionId != -1) { + listener.onResult(actionId) + } + } + return this + } + + private fun toggleLoadingOrContent(state: TrashbinFileActionsViewModel.UiState) { + if (state is TrashbinFileActionsViewModel.UiState.Loading) { + binding.bottomSheetLoading.isVisible = true + binding.bottomSheetHeader.isVisible = false + viewThemeUtils.platform.colorCircularProgressBar(binding.bottomSheetLoading, ColorRole.PRIMARY) + } else { + binding.bottomSheetLoading.isVisible = false + binding.bottomSheetHeader.isVisible = true + } + } + + private fun displayActions(actions: List) { + if (binding.fileActionsList.isEmpty()) { + actions.forEach { action -> + val view = inflateActionView(action) + binding.fileActionsList.addView(view) + } + } + } + + private fun displayTitle(titleFile: TrashbinFile?) { + titleFile?.fileName?.let { + binding.title.text = it + } ?: { binding.title.isVisible = false } + } + + private fun displayTitle(fileCount: Int) { + binding.title.text = resources.getQuantityString(R.plurals.trashbin_list__footer__file, fileCount, fileCount) + } + + private fun inflateActionView(action: TrashbinFileAction): View { + val itemBinding = FileActionsBottomSheetItemBinding.inflate(layoutInflater, binding.fileActionsList, false) + .apply { + root.setOnClickListener { + viewModel.onClick(action) + } + text.setText(action.title) + if (action.icon != null) { + val drawable = + viewThemeUtils.platform.tintDrawable( + requireContext(), + AppCompatResources.getDrawable(requireContext(), action.icon)!! + ) + icon.setImageDrawable(drawable) + } + } + return itemBinding.root + } + + private fun dispatchActionClick(id: Int?) { + if (id != null) { + setFragmentResult(REQUEST_KEY, bundleOf(RESULT_KEY_ACTION_ID to id)) + parentFragmentManager.clearFragmentResultListener(REQUEST_KEY) + dismiss() + } + } + + companion object { + private const val REQUEST_KEY = "REQUEST_KEY_ACTION" + private const val RESULT_KEY_ACTION_ID = "RESULT_KEY_ACTION_ID" + + @JvmStatic + fun newInstance(numberOfAllFiles: Int, files: Collection): TrashbinFileActionsBottomSheet = + TrashbinFileActionsBottomSheet().apply { + val argsBundle = bundleOf( + TrashbinFileActionsViewModel.ARG_ALL_FILES_COUNT to numberOfAllFiles, + TrashbinFileActionsViewModel.ARG_FILES to ArrayList(files) + ) + arguments = argsBundle + } + } +} diff --git a/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsViewModel.kt b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsViewModel.kt new file mode 100644 index 0000000..1558978 --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/trashbinFileActions/TrashbinFileActionsViewModel.kt @@ -0,0 +1,96 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.nextcloud.ui.trashbinFileActions + +import android.os.Bundle +import androidx.annotation.IdRes +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.nextcloud.client.logger.Logger +import com.nextcloud.ui.fileactions.FileActionsViewModel +import com.owncloud.android.R +import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import javax.inject.Inject + +class TrashbinFileActionsViewModel @Inject constructor(private val logger: Logger) : ViewModel() { + + sealed interface UiState { + data object Loading : UiState + data object Error : UiState + data class LoadedForSingleFile(val actions: List, val titleFile: TrashbinFile?) : UiState + + data class LoadedForMultipleFiles(val actions: List, val fileCount: Int) : UiState + } + + private val _uiState: MutableLiveData = MutableLiveData(UiState.Loading) + val uiState: LiveData + get() = _uiState + + private val _clickActionId: MutableLiveData = MutableLiveData(null) + val clickActionId: LiveData + @IdRes + get() = _clickActionId + + fun load(arguments: Bundle) { + val files: List? = arguments.getParcelableArrayList(ARG_FILES) + val numberOfAllFiles: Int = arguments.getInt(FileActionsViewModel.ARG_ALL_FILES_COUNT, 1) + + if (files.isNullOrEmpty()) { + logger.d(TAG, "No valid files argument for loading actions") + _uiState.postValue(UiState.Error) + } else { + load(files.toList(), numberOfAllFiles) + } + } + + private fun load(files: Collection, numberOfAllFiles: Int?) { + viewModelScope.launch(Dispatchers.IO) { + val toHide = getHiddenActions(numberOfAllFiles, files) + val availableActions = getActionsToShow(toHide) + updateStateLoaded(files, availableActions) + } + } + + private fun getHiddenActions(numberOfAllFiles: Int?, files: Collection): List { + numberOfAllFiles?.let { + if (files.size >= it) { + return listOf(R.id.action_select_all_action_menu) + } + } + + return listOf() + } + + private fun getActionsToShow(toHide: List) = TrashbinFileAction.SORTED_VALUES.filter { it.id !in toHide } + + private fun updateStateLoaded(files: Collection, availableActions: List) { + val state: UiState = when (files.size) { + 1 -> { + val file = files.first() + UiState.LoadedForSingleFile(availableActions, file) + } + + else -> UiState.LoadedForMultipleFiles(availableActions, files.size) + } + _uiState.postValue(state) + } + + fun onClick(action: TrashbinFileAction) { + _clickActionId.value = action.id + } + + companion object { + const val ARG_ALL_FILES_COUNT = "ALL_FILES_COUNT" + const val ARG_FILES = "FILES" + + private val TAG = TrashbinFileActionsViewModel::class.simpleName!! + } +} diff --git a/app/src/main/java/com/nextcloud/utils/BitmapExtensions.kt b/app/src/main/java/com/nextcloud/utils/BitmapExtensions.kt new file mode 100644 index 0000000..a3c70f9 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/BitmapExtensions.kt @@ -0,0 +1,119 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.graphics.Bitmap +import android.graphics.Matrix +import androidx.core.graphics.scale +import androidx.exifinterface.media.ExifInterface +import com.owncloud.android.lib.common.utils.Log_OC + +@Suppress("MagicNumber") +fun Bitmap.allocationKilobyte(): Int = allocationByteCount.div(1024) + +/** + * Recursively scales down the Bitmap until its size allocation is within the specified size. + * + * This function checks if the current Bitmap's size (in kilobytes) is already within + * the target size. If not, it scales the Bitmap down by a factor of `1.5` in both width and height + * and calls itself recursively until the size condition is met. + * + * @receiver Bitmap The original Bitmap to be resized. + * @param targetKB The target size in kilobytes (KB) that the Bitmap should be reduced to. + * @return A scaled-down Bitmap that meets the size allocation requirement. + */ +@Suppress("MagicNumber") +fun Bitmap.scaleUntil(targetKB: Int): Bitmap { + if (allocationKilobyte() <= targetKB) { + return this + } + + // 1.5 is used to gradually scale down while minimizing distortion + val scaleRatio = 1.5 + val width = width.div(scaleRatio).toInt() + val height = height.div(scaleRatio).toInt() + + val scaledBitmap = scale(width, height) + return scaledBitmap.scaleUntil(targetKB) +} + +/** + * Rotates and/or flips a [Bitmap] according to an EXIF orientation constant. + * + * Needed because loading bitmaps directly may ignore EXIF metadata with some devices, + * resulting in incorrectly displayed images. + * + * This function uses a [Matrix] transformation to adjust the image so that it + * appears upright when displayed. It supports all standard EXIF orientations, + * including mirrored and rotated cases. + * + * The original bitmap will be recycled if a new one is successfully created. + * If the device runs out of memory during the transformation, the original bitmap + * is returned unchanged. + * + * @receiver The [Bitmap] to rotate or flip. Can be `null`. + * @param orientation One of the [ExifInterface] orientation constants, such as + * [ExifInterface.ORIENTATION_ROTATE_90] or [ExifInterface.ORIENTATION_FLIP_HORIZONTAL]. + * @return The correctly oriented [Bitmap], or `null` if the receiver was `null`. + * + * @see ExifInterface + * @see Matrix + */ +@Suppress("MagicNumber", "ReturnCount") +fun Bitmap?.rotateBitmapViaExif(orientation: Int): Bitmap? { + if (this == null) { + return null + } + + val matrix = Matrix() + when (orientation) { + ExifInterface.ORIENTATION_NORMAL -> return this + ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.setScale(-1f, 1f) + ExifInterface.ORIENTATION_ROTATE_180 -> matrix.setRotate(180f) + ExifInterface.ORIENTATION_FLIP_VERTICAL -> { + matrix.setRotate(180f) + matrix.postScale(-1f, 1f) + } + + ExifInterface.ORIENTATION_TRANSPOSE -> { + matrix.setRotate(90f) + matrix.postScale(-1f, 1f) + } + + ExifInterface.ORIENTATION_ROTATE_90 -> matrix.setRotate(90f) + ExifInterface.ORIENTATION_TRANSVERSE -> { + matrix.setRotate(-90f) + matrix.postScale(-1f, 1f) + } + + ExifInterface.ORIENTATION_ROTATE_270 -> matrix.setRotate(-90f) + else -> return this + } + + return try { + val rotated = Bitmap.createBitmap( + this, + 0, + 0, + this.width, + this.height, + matrix, + true + ) + + // release original if a new one was created + if (rotated != this) { + this.recycle() + } + + rotated + } catch (_: OutOfMemoryError) { + Log_OC.e("BitmapExtension", "rotating bitmap, out of memory exception") + this + } +} diff --git a/app/src/main/java/com/nextcloud/utils/BuildHelper.kt b/app/src/main/java/com/nextcloud/utils/BuildHelper.kt new file mode 100644 index 0000000..4eca26f --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/BuildHelper.kt @@ -0,0 +1,15 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Tobias Kaminsky + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.nextcloud.utils + +import com.owncloud.android.BuildConfig + +object BuildHelper { + const val GPLAY: String = "gplay" + + fun isFlavourGPlay(): Boolean = GPLAY == BuildConfig.FLAVOR +} diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt new file mode 100644 index 0000000..a609071 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -0,0 +1,78 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.Manifest +import android.content.ContentUris +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.CalendarContract +import com.nextcloud.utils.extensions.showToast +import com.owncloud.android.R +import com.owncloud.android.lib.common.SearchResultEntry +import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface +import com.owncloud.android.utils.PermissionUtil.checkSelfPermission + +class CalendarEventManager(private val context: Context) { + + fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { + val havePermission = checkSelfPermission(context, Manifest.permission.READ_CALENDAR) + val createdAt = searchResult.createdAt() + val eventId: Long? = if (havePermission && createdAt != null) { + getCalendarEventId(searchResult.title, createdAt) + } else { + null + } + + if (eventId == null) { + val messageId = if (havePermission) { + R.string.unified_search_fragment_calendar_event_not_found + } else { + R.string.unified_search_fragment_permission_needed + } + context.showToast(messageId) + listInterface.onSearchResultClicked(searchResult) + } else { + val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId) + val intent = Intent(Intent.ACTION_VIEW).setData(uri) + context.startActivity(intent) + } + } + + private fun getCalendarEventId(eventTitle: String, eventStartDate: Long): Long? { + val projection = arrayOf( + CalendarContract.Events._ID, + CalendarContract.Events.TITLE, + CalendarContract.Events.DTSTART + ) + + val selection = "${CalendarContract.Events.TITLE} = ? AND ${CalendarContract.Events.DTSTART} = ?" + val selectionArgs = arrayOf(eventTitle, eventStartDate.toString()) + + val cursor = context.contentResolver.query( + CalendarContract.Events.CONTENT_URI, + projection, + selection, + selectionArgs, + "${CalendarContract.Events.DTSTART} ASC" + ) + + cursor?.use { + if (cursor.moveToFirst()) { + val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) + return cursor.getLong(idIndex) + } + } + + return null + } +} + +@Suppress("MagicNumber") +private fun SearchResultEntry.createdAt(): Long? = attributes["createdAt"]?.toLongOrNull()?.times(1000L) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt new file mode 100644 index 0000000..c956e17 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -0,0 +1,144 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.ContactsContract +import com.nextcloud.utils.extensions.showToast +import com.owncloud.android.R +import com.owncloud.android.lib.common.SearchResultEntry +import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface +import com.owncloud.android.utils.PermissionUtil.checkSelfPermission + +class ContactManager(private val context: Context) { + + fun openContact(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { + val havePermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) + val displayName = searchResult.displayName() + val contactId: Long? = if (havePermission && displayName != null) { + getContactIds(displayName).let { contactIds -> + if (contactIds.size > 1) getContactId(searchResult, contactIds) else contactIds.firstOrNull() + } + } else { + null + } + + if (contactId == null) { + val messageId = if (havePermission) { + R.string.unified_search_fragment_contact_not_found + } else { + R.string.unified_search_fragment_permission_needed + } + context.showToast(messageId) + listInterface.onSearchResultClicked(searchResult) + } else { + val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactId.toString()) + val intent = Intent(Intent.ACTION_VIEW).apply { + setData(uri) + } + context.startActivity(intent) + } + } + + private fun getContactId(searchResult: SearchResultEntry, contactIds: List): Long? { + val email = searchResult.email() + val phoneNumber = searchResult.phoneNumber() + + contactIds.forEach { + val targetEmail = getEmailById(it) ?: "" + val targetPhoneNumber = getPhoneNumberById(it) ?: "" + if (targetEmail == email && targetPhoneNumber == phoneNumber) { + return it + } + } + + return null + } + + private fun getEmailById(contactId: Long): String? { + var result: String? = null + val projection = arrayOf(ContactsContract.CommonDataKinds.Email.ADDRESS) + val selection = "${ContactsContract.CommonDataKinds.Email.CONTACT_ID} = ?" + val selectionArgs = arrayOf(contactId.toString()) + + val cursor = context.contentResolver.query( + ContactsContract.CommonDataKinds.Email.CONTENT_URI, + projection, + selection, + selectionArgs, + null + ) + + cursor?.use { + val emailIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS) + while (cursor.moveToNext()) { + result = cursor.getString(emailIndex) + } + } + + return result + } + + private fun getPhoneNumberById(contactId: Long): String? { + var result: String? = null + val projection = arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER) + val selection = "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?" + val selectionArgs = arrayOf(contactId.toString()) + + val cursor = context.contentResolver.query( + ContactsContract.CommonDataKinds.Phone.CONTENT_URI, + projection, + selection, + selectionArgs, + null + ) + + cursor?.use { + val phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) + while (cursor.moveToNext()) { + result = cursor.getString(phoneIndex) + } + } + + return result + } + + private fun getContactIds(displayName: String): List { + val result = arrayListOf() + val projection = arrayOf(ContactsContract.Contacts._ID) + val selection = "${ContactsContract.Contacts.DISPLAY_NAME} = ?" + val selectionArgs = arrayOf(displayName) + + val cursor = context.contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + projection, + selection, + selectionArgs, + null + ) + + cursor?.use { + val idIndex = cursor.getColumnIndex(ContactsContract.Contacts._ID) + while (cursor.moveToNext()) { + val id = cursor.getLong(idIndex) + result.add(id) + } + } + + return result + } +} + +private fun SearchResultEntry.displayName(): String? = attributes["displayName"] + +private fun SearchResultEntry.email(): String? = attributes["email"] + +private fun SearchResultEntry.phoneNumber(): String? = attributes["phoneNumber"] diff --git a/app/src/main/java/com/nextcloud/utils/EditorUtils.kt b/app/src/main/java/com/nextcloud/utils/EditorUtils.kt index f969992..c45e14e 100644 --- a/app/src/main/java/com/nextcloud/utils/EditorUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/EditorUtils.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils @@ -26,7 +26,5 @@ class EditorUtils @Inject constructor(private val arbitraryDataProvider: Arbitra ?: editors.firstOrNull { mimeType in it.optionalMimetypes } } - fun isEditorAvailable(user: User?, mimeType: String?): Boolean { - return getEditor(user, mimeType) != null - } + fun isEditorAvailable(user: User?, mimeType: String?): Boolean = getEditor(user, mimeType) != null } diff --git a/app/src/main/java/com/nextcloud/utils/FileHelper.kt b/app/src/main/java/com/nextcloud/utils/FileHelper.kt new file mode 100644 index 0000000..e417f88 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/FileHelper.kt @@ -0,0 +1,67 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import com.owncloud.android.lib.common.utils.Log_OC +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path +import java.util.stream.Collectors +import kotlin.io.path.pathString + +@Suppress("NestedBlockDepth") +object FileHelper { + private const val TAG = "FileHelper" + + fun listDirectoryEntries(directory: File?, startIndex: Int, maxItems: Int, fetchFolders: Boolean): List { + if (directory == null || !directory.exists() || !directory.isDirectory) return emptyList() + + return try { + Files.list(directory.toPath()) + .map { it.toFile() } + .filter { file -> if (fetchFolders) file.isDirectory else !file.isDirectory } + .skip(startIndex.toLong()) + .limit(maxItems.toLong()) + .collect(Collectors.toList()) + } catch (e: IOException) { + Log_OC.d(TAG, "listDirectoryEntries: $e") + emptyList() + } + } + + fun listFilesRecursive(files: Collection): List { + val result = mutableListOf() + + for (file in files) { + try { + collectFilesRecursively(file.toPath(), result) + } catch (e: IOException) { + Log_OC.e(TAG, "Error collecting files recursively from: ${file.absolutePath}", e) + } + } + + return result + } + + private fun collectFilesRecursively(path: Path, result: MutableList) { + if (Files.isDirectory(path)) { + try { + Files.newDirectoryStream(path).use { stream -> + for (entry in stream) { + collectFilesRecursively(entry, result) + } + } + } catch (e: IOException) { + Log_OC.e(TAG, "Error reading directory: ${path.pathString}", e) + } + } else { + result.add(path.pathString) + } + } +} diff --git a/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt b/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt index c01d644..9e41f4c 100644 --- a/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt +++ b/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils @@ -46,11 +46,9 @@ object ForegroundServiceHelper { id: Int, notification: Notification, foregroundServiceType: ForegroundServiceType - ): ForegroundInfo { - return if (isAboveOrEqualAndroid10) { - ForegroundInfo(id, notification, foregroundServiceType.getId()) - } else { - ForegroundInfo(id, notification) - } + ): ForegroundInfo = if (isAboveOrEqualAndroid10) { + ForegroundInfo(id, notification, foregroundServiceType.getId()) + } else { + ForegroundInfo(id, notification) } } diff --git a/app/src/main/java/com/nextcloud/utils/GlideHelper.kt b/app/src/main/java/com/nextcloud/utils/GlideHelper.kt new file mode 100644 index 0000000..ce89d91 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/GlideHelper.kt @@ -0,0 +1,193 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.graphics.drawable.PictureDrawable +import android.widget.ImageView +import androidx.annotation.DrawableRes +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory +import androidx.core.net.toUri +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestBuilder +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.load.model.GlideUrl +import com.bumptech.glide.load.model.LazyHeaders +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.BitmapImageViewTarget +import com.bumptech.glide.request.target.Target +import com.nextcloud.common.NextcloudClient +import com.nextcloud.utils.LinkHelper.validateAndGetURL +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter + +/** + * Utility object for loading images (including SVGs) using Glide. + * + * Provides methods for loading images into `ImageView`, `Target`, `Target` ... + * from both URLs and URIs. + */ +@Suppress("TooManyFunctions") +object GlideHelper { + private const val TAG = "GlideHelper" + + private class GlideLogger(private val methodName: String, private val identifier: String) : RequestListener { + override fun onLoadFailed(p0: GlideException?, p1: Any?, p2: Target, p3: Boolean): Boolean { + Log_OC.e(TAG, "$methodName: Load failed for $identifier") + Log_OC.e(TAG, "$methodName: Error: ${p0?.message}") + p0?.logRootCauses(TAG) + return false + } + + override fun onResourceReady(p0: T & Any, p1: Any, p2: Target?, p3: DataSource, p4: Boolean): Boolean { + Log_OC.i(TAG, "Glide load completed: $p0") + return false + } + } + + private fun isSVG(url: String): Boolean = (url.toUri().encodedPath?.endsWith(".svg") == true) + + private fun createGlideUrl(url: String, client: NextcloudClient) = GlideUrl( + url, + LazyHeaders.Builder() + .addHeader("Authorization", client.credentials) + .addHeader("User-Agent", "Mozilla/5.0 (Android) Nextcloud-android") + .build() + ) + + private fun RequestBuilder.withLogging(methodName: String, identifier: String): RequestBuilder = + listener(GlideLogger(methodName, identifier)) + + @SuppressLint("CheckResult") + private fun createSvgRequestBuilder( + context: Context, + uri: String, + client: NextcloudClient, + placeholder: Int? = null + ): RequestBuilder { + val glideUrl = createGlideUrl(uri, client) + + return Glide.with(context) + .`as`(PictureDrawable::class.java) + .load(glideUrl) + .apply { + placeholder?.let { placeholder(it) } + placeholder?.let { error(it) } + } + .listener(SvgSoftwareLayerSetter()) + } + + private fun createUrlRequestBuilder( + context: Context, + client: NextcloudClient, + url: String + ): RequestBuilder { + val glideUrl = createGlideUrl(url, client) + return Glide.with(context) + .load(glideUrl) + .centerCrop() + } + + @Suppress("TooGenericExceptionCaught") + fun getBitmap(context: Context, url: String?): Bitmap? { + val validatedUrl = validateAndGetURL(url) ?: return null + + return try { + Glide.with(context) + .asBitmap() + .load(validatedUrl) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .withLogging("downloadImageSynchronous", validatedUrl) + .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) + .get() + } catch (e: Exception) { + Log_OC.e(TAG, "Could not download image $e") + null + } + } + + fun loadCircularBitmapIntoImageView(context: Context, url: String?, imageView: ImageView, placeholder: Drawable) { + val validatedUrl = validateAndGetURL(url) ?: return + + Glide.with(context) + .asBitmap() + .load(validatedUrl) + .placeholder(placeholder) + .error(placeholder) + .withLogging("loadCircularBitmapIntoImageView", validatedUrl) + .into(object : BitmapImageViewTarget(imageView) { + override fun setResource(resource: Bitmap?) { + val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(context.resources, resource) + circularBitmapDrawable.isCircular = true + imageView.setImageDrawable(circularBitmapDrawable) + } + }) + } + + @Suppress("UNCHECKED_CAST", "TooGenericExceptionCaught", "ReturnCount") + private fun createRequestBuilder(context: Context, client: NextcloudClient?, url: String?): RequestBuilder? { + if (client == null) { + Log_OC.e(TAG, "Client is null") + return null + } + + val validatedUrl = validateAndGetURL(url) ?: return null + + return try { + val isSVG = isSVG(validatedUrl) + + return if (isSVG) { + createSvgRequestBuilder(context, validatedUrl, client) + } else { + createUrlRequestBuilder(context, client, validatedUrl) + } + .withLogging("createRequestBuilder", validatedUrl) as RequestBuilder? + } catch (e: Exception) { + Log_OC.e(TAG, "Error createRequestBuilder: $e") + null + } + } + + @SuppressLint("CheckResult") + fun loadIntoImageView( + context: Context, + client: NextcloudClient?, + url: String?, + imageView: ImageView, + @DrawableRes placeholder: Int, + circleCrop: Boolean = false + ) { + createRequestBuilder(context, client, url) + ?.placeholder(placeholder) + ?.error(placeholder) + ?.apply { if (circleCrop) circleCrop() } + ?.into(imageView) + } + + fun getDrawable(context: Context, client: NextcloudClient?, urlString: String?): Drawable? = + createRequestBuilder(context, client, urlString)?.submit()?.get() + + fun loadIntoTarget( + context: Context, + client: NextcloudClient?, + url: String, + target: Target, + @DrawableRes placeholder: Int + ) { + createRequestBuilder(context, client, url) + ?.placeholder(placeholder) + ?.error(placeholder) + ?.into(target) + } +} diff --git a/app/src/main/java/com/nextcloud/utils/LinkHelper.kt b/app/src/main/java/com/nextcloud/utils/LinkHelper.kt new file mode 100644 index 0000000..294c98d --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/LinkHelper.kt @@ -0,0 +1,135 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+ZetaTom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.core.net.toUri +import com.nextcloud.client.account.User +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.ui.activity.FileDisplayActivity +import java.util.Locale +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +object LinkHelper { + const val APP_NEXTCLOUD_NOTES = "it.niedermann.owncloud.notes" + const val APP_NEXTCLOUD_TALK = "com.nextcloud.talk2" + private const val TAG = "LinkHelper" + + fun isHttpOrHttpsLink(link: String?): Boolean = link?.lowercase(Locale.getDefault())?.let { + it.startsWith("http://") || it.startsWith("https://") + } == true + + /** + * Open specified app and, if not installed redirect to corresponding download. + * + * @param packageName of app to be opened + * @param user to pass in intent + */ + fun openAppOrStore(packageName: String, user: Optional, context: Context) { + openAppOrStore(packageName, user.getOrNull(), context) + } + + /** + * Open specified app and, if not installed redirect to corresponding download. + * + * @param packageName of app to be opened + * @param user to pass in intent + */ + fun openAppOrStore(packageName: String, user: User?, context: Context) { + val intent = context.packageManager.getLaunchIntentForPackage(packageName) + if (intent != null) { + // app installed - open directly + // TODO handle null user? + intent.putExtra(FileDisplayActivity.KEY_ACCOUNT, user.hashCode()) + context.startActivity(intent) + } else { + // app not found - open market (Google Play Store, F-Droid, etc.) + openAppStore(packageName, false, context) + } + } + + /** + * Open app store page of specified app or search for specified string. Will attempt to open browser when no app + * store is available. + * + * @param string packageName or url-encoded search string + * @param search false -> show app corresponding to packageName; true -> open search for string + */ + fun openAppStore(string: String, search: Boolean = false, context: Context) { + var suffix = (if (search) "search?q=" else "details?id=") + string + val intent = Intent(Intent.ACTION_VIEW, "market://$suffix".toUri()) + try { + context.startActivity(intent) + } catch (activityNotFoundException1: ActivityNotFoundException) { + // all is lost: open google play store web page for app + if (!search) { + suffix = "apps/$suffix" + } + intent.setData("https://play.google.com/store/$suffix".toUri()) + context.startActivity(intent) + } + } + + // region Validation + private const val HTTP = "http" + private const val HTTPS = "https" + private const val FILE = "file" + private const val CONTENT = "content" + + /** + * Validates if a string can be converted to a valid URI + */ + @Suppress("TooGenericExceptionCaught", "ReturnCount") + fun validateAndGetURI(uriString: String?): Uri? { + if (uriString.isNullOrBlank()) { + Log_OC.w(TAG, "Given uriString is null or blank") + return null + } + + return try { + val uri = uriString.toUri() + if (uri.scheme == null) { + return null + } + + val validSchemes = listOf(HTTP, HTTPS, FILE, CONTENT) + if (uri.scheme in validSchemes) uri else null + } catch (e: Exception) { + Log_OC.e(TAG, "Invalid URI string: $uriString -- $e") + null + } + } + + /** + * Validates if a URL string is valid + */ + @Suppress("TooGenericExceptionCaught", "ReturnCount") + fun validateAndGetURL(url: String?): String? { + if (url.isNullOrBlank()) { + Log_OC.w(TAG, "Given url is null or blank") + return null + } + + return try { + val uri = url.toUri() + if (uri.scheme == null) { + return null + } + val validSchemes = listOf(HTTP, HTTPS) + if (uri.scheme in validSchemes) url else null + } catch (e: Exception) { + Log_OC.e(TAG, "Invalid URL: $url -- $e") + null + } + } + // endregion +} diff --git a/app/src/main/java/com/nextcloud/utils/MenuUtils.kt b/app/src/main/java/com/nextcloud/utils/MenuUtils.kt index ce54614..56d150c 100644 --- a/app/src/main/java/com/nextcloud/utils/MenuUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/MenuUtils.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils diff --git a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt new file mode 100644 index 0000000..429de5a --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt @@ -0,0 +1,56 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.nextcloud.utils + +import androidx.exifinterface.media.ExifInterface +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.BitmapUtils + +object OCFileUtils { + private const val TAG = "OCFileUtils" + + @Suppress("ReturnCount", "NestedBlockDepth") + fun getImageSize(ocFile: OCFile, defaultThumbnailSize: Float): Pair { + try { + Log_OC.d(TAG, "Getting image size for: ${ocFile.fileName}") + + if (!ocFile.exists()) { + ocFile.imageDimension?.width?.let { w -> + ocFile.imageDimension?.height?.let { h -> + return w.toInt() to h.toInt() + } + } + val size = defaultThumbnailSize.toInt().coerceAtLeast(1) + return size to size + } + + val exif = ExifInterface(ocFile.storagePath) + val width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) + val height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) + + if (width > 0 && height > 0) { + Log_OC.d(TAG, "Exif used width: $width and height: $height") + return width to height + } + + val (bitmapWidth, bitmapHeight) = BitmapUtils.getImageResolution(ocFile.storagePath) + .let { it[0] to it[1] } + + if (bitmapWidth > 0 && bitmapHeight > 0) { + Log_OC.d(TAG, "BitmapUtils.getImageResolution used width: $bitmapWidth and height: $bitmapHeight") + return bitmapWidth to bitmapHeight + } + + val fallback = defaultThumbnailSize.toInt().coerceAtLeast(1) + Log_OC.d(TAG, "Default size used width: $fallback and height: $fallback") + return fallback to fallback + } finally { + Log_OC.d(TAG, "-----------------------------") + } + } +} diff --git a/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt b/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt index 16b8bc3..e2ad9e5 100644 --- a/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt +++ b/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt @@ -1,10 +1,10 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Felix Nüsse - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils @@ -14,12 +14,13 @@ import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.createBitmap import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toBitmap +import androidx.core.graphics.drawable.toDrawable import com.nextcloud.client.account.User import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile @@ -44,52 +45,62 @@ class ShortcutUtil @Inject constructor(private val mContext: Context) { user: User, syncedFolderProvider: SyncedFolderProvider ) { - if (ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)) { - val intent = Intent(mContext, FileDisplayActivity::class.java) - intent.action = FileDisplayActivity.OPEN_FILE - intent.putExtra(FileActivity.EXTRA_FILE, file.remotePath) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - val shortcutId = "nextcloud_shortcut_" + file.remoteId - val icon: IconCompat - var thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.remoteId - ) - if (thumbnail != null) { - thumbnail = bitmapToAdaptiveBitmap(thumbnail) - icon = IconCompat.createWithAdaptiveBitmap(thumbnail) - } else if (file.isFolder) { + if (!ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)) { + return + } + + val intent = Intent(mContext, FileDisplayActivity::class.java).apply { + action = FileDisplayActivity.OPEN_FILE + putExtra(FileActivity.EXTRA_FILE_REMOTE_PATH, file.decryptedRemotePath) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + } + + val icon = createShortcutIcon(file, viewThemeUtils, user, syncedFolderProvider) + + val shortcutInfo = ShortcutInfoCompat.Builder(mContext, "nextcloud_shortcut_${file.remoteId}") + .setShortLabel(file.fileName) + .setLongLabel(mContext.getString(R.string.pin_shortcut_label, file.fileName)) + .setIcon(icon) + .setIntent(intent) + .build() + + val resultIntent = + ShortcutManagerCompat.createShortcutResultIntent(mContext, shortcutInfo) + + val pendingIntent = PendingIntent.getBroadcast( + mContext, + file.hashCode(), + resultIntent, + FLAG_IMMUTABLE + ) + + ShortcutManagerCompat.requestPinShortcut(mContext, shortcutInfo, pendingIntent.intentSender) + } + + private fun createShortcutIcon( + file: OCFile, + viewThemeUtils: ViewThemeUtils, + user: User, + syncedFolderProvider: SyncedFolderProvider + ): IconCompat { + val thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.remoteId + ) + + return when { + thumbnail != null -> IconCompat.createWithAdaptiveBitmap(bitmapToAdaptiveBitmap(thumbnail)) + + file.isFolder -> { val isAutoUploadFolder = SyncedFolderProvider.isAutoUploadFolder(syncedFolderProvider, file, user) val isDarkModeActive = syncedFolderProvider.preferences.isDarkModeEnabled - val overlayIconId = file.getFileOverlayIconId(isAutoUploadFolder) - val drawable = MimeTypeUtil.getFileIcon(isDarkModeActive, overlayIconId, mContext, viewThemeUtils) - val bitmapIcon = drawable.toBitmap() - icon = IconCompat.createWithBitmap(bitmapIcon) - } else { - icon = IconCompat.createWithResource( - mContext, - MimeTypeUtil.getFileTypeIconId(file.mimeType, file.fileName) - ) + val drawable = MimeTypeUtil.getFolderIcon(isDarkModeActive, overlayIconId, mContext, viewThemeUtils) + IconCompat.createWithBitmap(drawable.toBitmap()) } - val longLabel = mContext.getString(R.string.pin_shortcut_label, file.fileName) - val pinShortcutInfo = ShortcutInfoCompat.Builder(mContext, shortcutId) - .setShortLabel(file.fileName) - .setLongLabel(longLabel) - .setIcon(icon) - .setIntent(intent) - .build() - val pinnedShortcutCallbackIntent = - ShortcutManagerCompat.createShortcutResultIntent(mContext, pinShortcutInfo) - val successCallback = PendingIntent.getBroadcast( + + else -> IconCompat.createWithResource( mContext, - 0, - pinnedShortcutCallbackIntent, - FLAG_IMMUTABLE - ) - ShortcutManagerCompat.requestPinShortcut( - mContext, - pinShortcutInfo, - successCallback.intentSender + MimeTypeUtil.getFileTypeIconId(file.mimeType, file.fileName) ) } } @@ -97,8 +108,8 @@ class ShortcutUtil @Inject constructor(private val mContext: Context) { private fun bitmapToAdaptiveBitmap(orig: Bitmap): Bitmap { val adaptiveIconSize = mContext.resources.getDimensionPixelSize(R.dimen.adaptive_icon_size) val adaptiveIconOuterSides = mContext.resources.getDimensionPixelSize(R.dimen.adaptive_icon_padding) - val drawable: Drawable = BitmapDrawable(mContext.resources, orig) - val bitmap = Bitmap.createBitmap(adaptiveIconSize, adaptiveIconSize, Bitmap.Config.ARGB_8888) + val drawable: Drawable = orig.toDrawable(mContext.resources) + val bitmap = createBitmap(adaptiveIconSize, adaptiveIconSize) val canvas = Canvas(bitmap) drawable.setBounds( adaptiveIconOuterSides, diff --git a/app/src/main/java/com/nextcloud/utils/TimeConstants.kt b/app/src/main/java/com/nextcloud/utils/TimeConstants.kt index 7663059..7c57976 100644 --- a/app/src/main/java/com/nextcloud/utils/TimeConstants.kt +++ b/app/src/main/java/com/nextcloud/utils/TimeConstants.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils diff --git a/app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt b/app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt new file mode 100644 index 0000000..11dba9a --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt @@ -0,0 +1,135 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.autoRename + +import com.nextcloud.utils.extensions.StringConstants +import com.nextcloud.utils.extensions.checkWCFRestrictions +import com.nextcloud.utils.extensions.forbiddenFilenameCharacters +import com.nextcloud.utils.extensions.forbiddenFilenameExtensions +import com.nextcloud.utils.extensions.shouldRemoveNonPrintableUnicodeCharactersAndConvertToUTF8 +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.status.OCCapability +import org.apache.commons.io.FilenameUtils +import java.util.regex.Pattern + +object AutoRename { + private const val TAG = "AutoRename" + private const val REPLACEMENT = "_" + + @Suppress("NestedBlockDepth") + @JvmOverloads + fun rename(filename: String, capability: OCCapability, isFolderPath: Boolean? = null): String { + if (!capability.checkWCFRestrictions()) { + return filename + } + + Log_OC.d(TAG, "Before - $filename") + + val isFolder = isFolderPath ?: filename.endsWith(OCFile.PATH_SEPARATOR) + val pathSegments = filename.split(OCFile.PATH_SEPARATOR).toMutableList() + + capability.run { + if (forbiddenFilenameCharactersJson != null) { + var forbiddenFilenameCharacters = capability.forbiddenFilenameCharacters() + + if (isFolder) { + forbiddenFilenameCharacters = forbiddenFilenameCharacters.filter { it != OCFile.PATH_SEPARATOR } + } + + pathSegments.replaceAll { segment -> + var modifiedSegment = segment + + forbiddenFilenameCharacters.forEach { forbiddenChar -> + if (modifiedSegment.contains(forbiddenChar)) { + modifiedSegment = modifiedSegment.replace(forbiddenChar, REPLACEMENT) + } + } + + modifiedSegment + } + } + + if (forbiddenFilenameExtensionJson != null) { + val forbiddenFilenameExtensions = forbiddenFilenameExtensions() + + forbiddenFilenameExtensions.find { it == StringConstants.SPACE }?.let { + pathSegments.replaceAll { segment -> + segment.trim() + } + } + + forbiddenFilenameExtensions.find { it == StringConstants.DOT }?.let { forbiddenExtension -> + pathSegments.replaceAll { segment -> + replaceDots(forbiddenExtension, segment) + } + } + + forbiddenFilenameExtensions + .filter { it != StringConstants.SPACE && it != StringConstants.DOT } + .forEach { forbiddenExtension -> + pathSegments.replaceAll { segment -> + replaceFileExtensions(forbiddenExtension, segment) + } + } + } + } + + val filenameWithExtension = pathSegments.joinToString(OCFile.PATH_SEPARATOR) + val updatedFileName = if (isFolder) filenameWithExtension else lowercaseFileExtension(filenameWithExtension) + + val result = if (capability.shouldRemoveNonPrintableUnicodeCharactersAndConvertToUTF8()) { + val utf8Result = convertToUTF8(updatedFileName) + removeNonPrintableUnicodeCharacters(utf8Result) + } else { + updatedFileName + }.trim() + + Log_OC.d(TAG, "After - $result") + + return result + } + + private fun lowercaseFileExtension(filename: String): String { + val extension = FilenameUtils.getExtension(filename).lowercase() + val filenameWithoutExtension = FilenameUtils.removeExtension(filename) + return if (extension.isNotEmpty()) { + filenameWithoutExtension + StringConstants.DOT + extension + } else { + filenameWithoutExtension + } + } + + private fun replaceDots(forbiddenExtension: String, segment: String): String = + if (isSegmentContainsForbiddenExtension(forbiddenExtension, segment)) { + segment.replaceFirst(forbiddenExtension, REPLACEMENT) + } else { + segment + } + + private fun replaceFileExtensions(forbiddenExtension: String, segment: String): String = + if (isSegmentContainsForbiddenExtension(forbiddenExtension, segment)) { + val newExtension = forbiddenExtension.replace(StringConstants.DOT, REPLACEMENT, ignoreCase = true) + segment.replace(forbiddenExtension, newExtension.lowercase(), ignoreCase = true) + } else { + segment + } + + private fun isSegmentContainsForbiddenExtension(forbiddenExtension: String, segment: String): Boolean = + segment.endsWith(forbiddenExtension, ignoreCase = true) || + segment.startsWith(forbiddenExtension, ignoreCase = true) + + private fun convertToUTF8(filename: String): String = String(filename.toByteArray(), Charsets.UTF_8) + + private fun removeNonPrintableUnicodeCharacters(filename: String): String { + val regex = "\\p{C}" + val pattern = Pattern.compile(regex) + val matcher = pattern.matcher(filename) + return matcher.replaceAll("") + } +} diff --git a/app/src/main/java/com/nextcloud/utils/date/DateFormatPattern.kt b/app/src/main/java/com/nextcloud/utils/date/DateFormatPattern.kt new file mode 100644 index 0000000..a3852d6 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/date/DateFormatPattern.kt @@ -0,0 +1,20 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.date + +enum class DateFormatPattern(val pattern: String) { + /** + * 10.11.2024 - 12:44 + */ + FullDateWithHours("dd.MM.yyyy - HH:mm"), + + /** + * Aug 3 + */ + MonthWithDate("MMM d") +} diff --git a/app/src/main/java/com/nextcloud/utils/date/DateFormatter.kt b/app/src/main/java/com/nextcloud/utils/date/DateFormatter.kt new file mode 100644 index 0000000..ea43a5a --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/date/DateFormatter.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.date + +import android.icu.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +object DateFormatter { + + /** + * Converts a Unix timestamp (in milliseconds) into a formatted date string. + * For example, input 1733309160885 with "MMM d" pattern outputs "Dec 4". + */ + @Suppress("MagicNumber") + fun timestampToDateRepresentation(timestamp: Long, formatPattern: DateFormatPattern): String { + val date = Date(timestamp * 1000) + val format = SimpleDateFormat(formatPattern.pattern, Locale.getDefault()) + return format.format(date) + } +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt index c17f93c..2f70ada 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ActionBarExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ActionBarExtensions.kt new file mode 100644 index 0000000..3bafc9b --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/ActionBarExtensions.kt @@ -0,0 +1,19 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import android.text.Spannable +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import androidx.appcompat.app.ActionBar + +fun ActionBar.setTitleColor(color: Int) { + val text = SpannableString(title ?: "") + text.setSpan(ForegroundColorSpan(color), 0, text.length, Spannable.SPAN_INCLUSIVE_INCLUSIVE) + title = text +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ActivityExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ActivityExtensions.kt index c60390f..19c9686 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ActivityExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ActivityExtensions.kt @@ -7,9 +7,25 @@ package com.nextcloud.utils.extensions +import android.app.Activity +import android.content.Intent import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment -fun AppCompatActivity.isDialogFragmentReady(fragment: Fragment): Boolean = isActive() && !fragment.isStateSaved() +fun AppCompatActivity.isDialogFragmentReady(fragment: Fragment): Boolean = isActive() && !fragment.isStateSaved fun AppCompatActivity.isActive(): Boolean = !isFinishing && !isDestroyed + +fun AppCompatActivity.fragments(): List = supportFragmentManager.fragments + +fun AppCompatActivity.lastFragment(): Fragment? = supportFragmentManager.fragments.lastOrNull { it.isVisible } + +fun Activity.showShareIntent(text: String?) { + val sendIntent = Intent(Intent.ACTION_SEND).apply { + putExtra(Intent.EXTRA_TEXT, text) + type = "text/plain" + } + + val shareIntent = Intent.createChooser(sendIntent, null) + startActivity(shareIntent) +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt index aa0cccc..d6ddbf4 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt @@ -1,41 +1,23 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions -import android.os.Build import android.os.Bundle import android.os.Parcelable -import com.owncloud.android.lib.common.utils.Log_OC +import androidx.core.os.BundleCompat import java.io.Serializable -@Suppress("TopLevelPropertyNaming") -private const val tag = "BundleExtension" - fun Bundle?.getSerializableArgument(key: String, type: Class): T? { if (this == null) { return null } - return try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - this.getSerializable(key, type) - } else { - @Suppress("UNCHECKED_CAST", "DEPRECATION") - if (type.isInstance(this.getSerializable(key))) { - this.getSerializable(key) as T - } else { - null - } - } - } catch (e: ClassCastException) { - Log_OC.e(tag, e.localizedMessage) - null - } + return BundleCompat.getSerializable(this, key, type) } fun Bundle?.getParcelableArgument(key: String, type: Class): T? { @@ -43,16 +25,5 @@ fun Bundle?.getParcelableArgument(key: String, type: Class) return null } - return try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - this.getParcelable(key, type) - } else { - @Suppress("DEPRECATION") - this.getParcelable(key) - } - } catch (e: ClassCastException) { - Log_OC.e(tag, e.localizedMessage) - e.printStackTrace() - null - } + return BundleCompat.getParcelable(this, key, type) } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt index e1ff053..6a2441b 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt @@ -1,25 +1,71 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions import android.annotation.SuppressLint +import android.app.Activity import android.content.BroadcastReceiver import android.content.Context +import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter import android.os.Build +import android.os.Handler +import android.os.Looper +import android.view.WindowInsets +import android.view.WindowManager +import android.widget.Toast +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.owncloud.android.R import com.owncloud.android.datamodel.ReceiverFlag +fun Context.hourPlural(hour: Int): String = resources.getQuantityString(R.plurals.hours, hour, hour) + +fun Context.minPlural(min: Int): String = resources.getQuantityString(R.plurals.minutes, min, min) + @SuppressLint("UnspecifiedRegisterReceiverFlag") -fun Context.registerBroadcastReceiver(receiver: BroadcastReceiver?, filter: IntentFilter, flag: ReceiverFlag): Intent? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +fun Context.registerBroadcastReceiver(receiver: BroadcastReceiver?, filter: IntentFilter, flag: ReceiverFlag): Intent? = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(receiver, filter, flag.getId()) } else { registerReceiver(receiver, filter) } + +fun Context.statusBarHeight(): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val windowInsets = (getSystemService(Context.WINDOW_SERVICE) as WindowManager) + .currentWindowMetrics + .windowInsets + val insets = windowInsets.getInsets(WindowInsets.Type.statusBars()) + insets.top +} else { + @Suppress("DEPRECATION") + val decorView = (getSystemService(Context.WINDOW_SERVICE) as WindowManager) + .defaultDisplay + .let { display -> + val decorView = android.view.View(this) + display.getRealMetrics(android.util.DisplayMetrics()) + decorView + } + val windowInsetsCompat = ViewCompat.getRootWindowInsets(decorView) + windowInsetsCompat?.getInsets(WindowInsetsCompat.Type.statusBars())?.top ?: 0 +} + +fun Context.showToast(message: String) { + Handler(Looper.getMainLooper()).post { + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } +} + +fun Context.showToast(messageId: Int) = showToast(getString(messageId)) + +fun Context.getActivity(): Activity? = when (this) { + is Activity -> this + is ContextWrapper -> baseContext.getActivity() + else -> null } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/DateExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/DateExtensions.kt new file mode 100644 index 0000000..cda0532 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/DateExtensions.kt @@ -0,0 +1,17 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import android.annotation.SuppressLint +import com.nextcloud.utils.date.DateFormatPattern +import java.text.SimpleDateFormat +import java.util.Date + +@SuppressLint("SimpleDateFormat") +fun Date.currentDateRepresentation(formatPattern: DateFormatPattern): String = + SimpleDateFormat(formatPattern.pattern).format(this) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/DecryptedUserExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/DecryptedUserExtensions.kt new file mode 100644 index 0000000..ba48306 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/DecryptedUserExtensions.kt @@ -0,0 +1,22 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser + +fun List.findMetadataKeyByUserId(userId: String): String? { + var result: String? = null + + for (decryptedUser in this) { + if (decryptedUser != null && decryptedUser.userId == userId) { + result = decryptedUser.decryptedMetadataKey + } + } + + return result +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/DrawableExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/DrawableExtensions.kt new file mode 100644 index 0000000..3fcefbb --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/DrawableExtensions.kt @@ -0,0 +1,20 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.PictureDrawable +import androidx.core.graphics.createBitmap + +fun PictureDrawable.toBitmap(): Bitmap { + val bitmap = createBitmap(picture.getWidth(), picture.getHeight()) + val canvas = Canvas(bitmap) + picture.draw(canvas) + return bitmap +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt new file mode 100644 index 0000000..d096603 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt @@ -0,0 +1,43 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import android.content.Intent +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.ui.activity.DrawerActivity +import com.owncloud.android.ui.activity.FileDisplayActivity + +@Suppress("ReturnCount") +fun DrawerActivity.handleBackButtonEvent(currentDir: OCFile): Boolean { + if (DrawerActivity.menuItemId == R.id.nav_all_files && currentDir.isRootDirectory) { + moveTaskToBack(true) + return true + } + + val isParentDirExists = (storageManager.getFileById(currentDir.parentId) != null) + if (isParentDirExists) { + return false + } + + DrawerActivity.menuItemId = R.id.nav_all_files + setNavigationViewItemChecked() + + MainApp.showOnlyFilesOnDevice(false) + MainApp.showOnlyPersonalFiles(false) + + Intent(applicationContext, FileDisplayActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + action = FileDisplayActivity.ALL_FILES + }.run { + startActivity(this) + } + + return true +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt index b9ff09c..1194301 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/Extensions.kt @@ -1,9 +1,10 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2024 TSI-mc * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions @@ -17,6 +18,8 @@ import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.view.View import android.widget.TextView +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -79,3 +82,12 @@ fun Long.getFormattedStringDate(format: String): String { val simpleDateFormat = SimpleDateFormat(format, Locale.getDefault()) return simpleDateFormat.format(Date(this)) } + +fun TrashbinFile.toOCFile(): OCFile { + val ocFile = OCFile(this.remotePath) + ocFile.mimeType = this.mimeType + ocFile.fileLength = this.fileLength + ocFile.remoteId = this.remoteId + ocFile.fileName = this.fileName + return ocFile +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt new file mode 100644 index 0000000..aea6376 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt @@ -0,0 +1,40 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile + +fun FileDataStorageManager.getParentIdsOfSubfiles(paths: List): List = + fileDao.getParentIdsOfSubfiles(paths) + +fun FileDataStorageManager.getDecryptedPath(file: OCFile): String { + val paths = mutableListOf() + var entity = fileDao.getFileByEncryptedRemotePath(file.remotePath, user.accountName) + + while (entity != null) { + entity.name?.takeIf { it.isNotEmpty() }?.let { + paths.add(it.removePrefix(OCFile.PATH_SEPARATOR)) + } + entity = entity.parent?.let { fileDao.getFileById(it) } ?: break + } + + return paths + .reversed() + .joinToString(OCFile.PATH_SEPARATOR) +} + +fun FileDataStorageManager.getSubfiles(id: Long, accountName: String): List = + fileDao.getSubfiles(id, accountName).map { + createFileInstance(it) + } + +fun FileDataStorageManager.getNonEncryptedSubfolders(id: Long, accountName: String): List = + fileDao.getNonEncryptedSubfolders(id, accountName).map { + createFileInstance(it) + } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt new file mode 100644 index 0000000..94ae83b --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt @@ -0,0 +1,25 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.DisplayUtils +import java.io.File + +fun OCFile?.logFileSize(tag: String) { + val size = DisplayUtils.bytesToHumanReadable(this?.fileLength ?: -1) + val rawByte = this?.fileLength ?: -1 + Log_OC.d(tag, "onSaveInstanceState: $size, raw byte $rawByte") +} + +fun File?.logFileSize(tag: String) { + val size = DisplayUtils.bytesToHumanReadable(this?.length() ?: -1) + val rawByte = this?.length() ?: -1 + Log_OC.d(tag, "onSaveInstanceState: $size, raw byte $rawByte") +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/FragmentExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/FragmentExtensions.kt new file mode 100644 index 0000000..fafa7fd --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/FragmentExtensions.kt @@ -0,0 +1,30 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle + +inline fun Fragment.typedActivity(): T? = if (isAdded && activity != null && activity is T) { + activity as T +} else { + null +} + +/** + * Extension for Java Classes + */ +fun Fragment.getTypedActivity(type: Class): T? = + if (isAdded && activity != null && type.isInstance(activity)) { + type.cast(activity) + } else { + null + } + +fun Fragment.isDialogFragmentReady() = + isAdded && !isStateSaved && activity?.lifecycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt new file mode 100644 index 0000000..896d26d --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt @@ -0,0 +1,49 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import android.content.Context +import android.graphics.drawable.GradientDrawable +import android.util.TypedValue +import android.view.ViewOutlineProvider +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.DrawableRes +import androidx.core.content.ContextCompat +import com.owncloud.android.R + +@JvmOverloads +fun ImageView.makeRoundedWithIcon( + context: Context, + @DrawableRes icon: Int, + paddingDp: Int = 6, + @ColorInt backgroundColor: Int = ContextCompat.getColor(context, R.color.primary), + @ColorInt foregroundColor: Int = ContextCompat.getColor(context, R.color.white) +) { + setImageResource(icon) + + val drawable = GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(backgroundColor) + } + + background = drawable + clipToOutline = true + scaleType = ImageView.ScaleType.CENTER_INSIDE + outlineProvider = ViewOutlineProvider.BACKGROUND + + setColorFilter(foregroundColor) + + val paddingPx = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + paddingDp.toFloat(), + context.resources.displayMetrics + ).toInt() + + setPadding(paddingPx, paddingPx, paddingPx, paddingPx) +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/IntExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/IntExtensions.kt new file mode 100644 index 0000000..df10cbc --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/IntExtensions.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import java.nio.ByteBuffer + +@Suppress("MagicNumber") +fun IntArray.toByteArray(): ByteArray { + val byteBuffer = ByteBuffer.allocate(this.size * 4) + val intBuffer = byteBuffer.asIntBuffer() + intBuffer.put(this) + return byteBuffer.array() +} + +@Suppress("MagicNumber") +fun ByteArray.toIntArray(): IntArray { + val intBuffer = ByteBuffer.wrap(this).asIntBuffer() + val intArray = IntArray(this.size / 4) + intBuffer.get(intArray) + return intArray +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt index d709068..56da87f 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt @@ -1,41 +1,23 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions import android.content.Intent -import android.os.Build import android.os.Parcelable -import com.owncloud.android.lib.common.utils.Log_OC +import androidx.core.content.IntentCompat import java.io.Serializable -@Suppress("TopLevelPropertyNaming") -private const val tag = "IntentExtension" - fun Intent?.getSerializableArgument(key: String, type: Class): T? { if (this == null) { return null } - return try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - this.getSerializableExtra(key, type) - } else { - @Suppress("UNCHECKED_CAST", "DEPRECATION") - if (type.isInstance(this.getSerializableExtra(key))) { - this.getSerializableExtra(key) as T - } else { - null - } - } - } catch (e: ClassCastException) { - Log_OC.e(tag, e.localizedMessage) - null - } + return IntentCompat.getSerializableExtra(this, key, type) } fun Intent?.getParcelableArgument(key: String, type: Class): T? { @@ -43,15 +25,5 @@ fun Intent?.getParcelableArgument(key: String, type: Class) return null } - return try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - this.getParcelableExtra(key, type) - } else { - @Suppress("DEPRECATION") - this.getParcelableExtra(key) - } - } catch (e: ClassCastException) { - Log_OC.e(tag, e.localizedMessage) - null - } + return IntentCompat.getParcelableExtra(this, key, type) } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCCapabilityExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCCapabilityExtensions.kt new file mode 100644 index 0000000..8628548 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCCapabilityExtensions.kt @@ -0,0 +1,59 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.google.gson.Gson +import com.owncloud.android.lib.resources.status.NextcloudVersion +import com.owncloud.android.lib.resources.status.OCCapability +import org.json.JSONException + +private val gson = Gson() + +/** + * Determines whether **Windows-compatible file (WCF)** restrictions should be applied + * for the current server version and configuration. + * + * Behavior: + * - For **Nextcloud 32 and newer**, WCF enforcement depends on the [`isWCFEnabled`] flag + * provided by the server capabilities. + * - For **Nextcloud 30 and 31**, WCF restrictions are always applied (feature considered enabled). + * - For **versions older than 30**, WCF is not supported, and no restrictions are applied. + * + * @return `true` if WCF restrictions should be enforced based on the server version and configuration; + * `false` otherwise. + */ +fun OCCapability.checkWCFRestrictions(): Boolean = if (version.isNewerOrEqual(NextcloudVersion.nextcloud_32)) { + isWCFEnabled.isTrue +} else { + version.isNewerOrEqual(NextcloudVersion.nextcloud_30) +} + +fun OCCapability.forbiddenFilenames(): List = jsonToList(forbiddenFilenamesJson) + +fun OCCapability.forbiddenFilenameCharacters(): List = jsonToList(forbiddenFilenameCharactersJson) + +fun OCCapability.forbiddenFilenameExtensions(): List = jsonToList(forbiddenFilenameExtensionJson) + +fun OCCapability.forbiddenFilenameBaseNames(): List = jsonToList(forbiddenFilenameBaseNamesJson) + +fun OCCapability.shouldRemoveNonPrintableUnicodeCharactersAndConvertToUTF8(): Boolean = + forbiddenFilenames().isNotEmpty() || + forbiddenFilenameCharacters().isNotEmpty() || + forbiddenFilenameExtensions().isNotEmpty() || + forbiddenFilenameBaseNames().isNotEmpty() + +@Suppress("ReturnCount") +private fun jsonToList(json: String?): List { + if (json == null) return emptyList() + + return try { + return gson.fromJson(json, Array::class.java).toList() + } catch (_: JSONException) { + emptyList() + } +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt new file mode 100644 index 0000000..1e32739 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt @@ -0,0 +1,33 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.MainApp +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.utils.FileStorageUtils + +fun List.filterFilenames(): List = distinctBy { it.fileName } + +fun List.filterTempFilter(): List = filterNot { it.isTempFile() } + +fun OCFile.isTempFile(): Boolean { + val context = MainApp.getAppContext() + val appTempPath = FileStorageUtils.getAppTempDirectoryPath(context) + return storagePath?.startsWith(appTempPath) == true +} + +fun List.filterHiddenFiles(): List = filterNot { it.isHidden }.distinct() + +fun List.filterByMimeType(mimeType: String): List = + filter { it.isFolder || it.mimeType.startsWith(mimeType) } + +fun List.limitToPersonalFiles(userId: String): List = filter { file -> + file.ownerId?.let { ownerId -> + ownerId == userId && !file.isSharedWithMe && !file.mounted() + } == true +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt new file mode 100644 index 0000000..9161bf9 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCShareExtensions.kt @@ -0,0 +1,14 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.lib.resources.shares.OCShare + +fun OCShare.hasFileRequestPermission(): Boolean = (isFolder && shareType?.isPublicOrMail() == true) + +fun List.mergeDistinctByToken(other: List): List = (this + other).distinctBy { it.token } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCUploadExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCUploadExtensions.kt new file mode 100644 index 0000000..6b2550d --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCUploadExtensions.kt @@ -0,0 +1,14 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.db.OCUpload + +fun List.getUploadIds(): LongArray = map { it.uploadId }.toLongArray() + +fun Array.getUploadIds(): LongArray = map { it.uploadId }.toLongArray() diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OnDataTransferProgressListenerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OnDataTransferProgressListenerExtensions.kt new file mode 100644 index 0000000..f6fed6f --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/OnDataTransferProgressListenerExtensions.kt @@ -0,0 +1,14 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.lib.common.network.OnDatatransferProgressListener + +@Suppress("MagicNumber") +fun OnDatatransferProgressListener.getPercent(totalTransferredSoFar: Long, totalToTransfer: Long): Int = + ((100.0 * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt()).coerceAtMost(100) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt new file mode 100644 index 0000000..ee36c24 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt @@ -0,0 +1,22 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2023 ZetaTom + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ + +package com.nextcloud.utils.extensions + +import android.content.Context +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.OwnCloudClientFactory + +fun OwnCloudClient.toNextcloudClient(context: Context): NextcloudClient = OwnCloudClientFactory.createNextcloudClient( + baseUri, + userId, + credentials.toOkHttpCredentials(), + context, + isFollowRedirects +) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ParcableExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ParcableExtensions.kt new file mode 100644 index 0000000..86e1a26 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/ParcableExtensions.kt @@ -0,0 +1,20 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import android.os.Parcel +import android.os.Parcelable +import androidx.core.os.ParcelCompat + +inline fun Parcel?.readParcelableCompat(classLoader: ClassLoader?): T? { + if (this == null) { + return null + } + + return ParcelCompat.readParcelable(this, classLoader, T::class.java) +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt new file mode 100644 index 0000000..caf0ad8 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt @@ -0,0 +1,64 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.resources.files.model.RemoteFile +import com.owncloud.android.utils.ErrorMessageAdapter +import com.owncloud.android.utils.FileStorageUtils + +@Suppress("ReturnCount") +fun Pair?, RemoteOperation<*>?>?.getErrorMessage(): String { + val result = this?.first ?: return MainApp.string(R.string.unexpected_error_occurred) + val operation = this.second ?: return MainApp.string(R.string.unexpected_error_occurred) + return ErrorMessageAdapter.getErrorCauseMessage(result, operation, MainApp.getAppContext().resources) +} + +fun ResultCode.isFileSpecificError(): Boolean { + val errorCodes = listOf( + ResultCode.INSTANCE_NOT_CONFIGURED, + ResultCode.QUOTA_EXCEEDED, + ResultCode.LOCAL_STORAGE_FULL, + ResultCode.WRONG_CONNECTION, + ResultCode.UNAUTHORIZED, + ResultCode.OK_NO_SSL, + ResultCode.MAINTENANCE_MODE, + ResultCode.UNTRUSTED_DOMAIN, + ResultCode.ACCOUNT_NOT_THE_SAME, + ResultCode.ACCOUNT_EXCEPTION, + ResultCode.ACCOUNT_NOT_NEW, + ResultCode.ACCOUNT_NOT_FOUND, + ResultCode.ACCOUNT_USES_STANDARD_PASSWORD, + ResultCode.INCORRECT_ADDRESS, + ResultCode.BAD_OC_VERSION + ) + + return !errorCodes.contains(this) +} + +@Suppress("Deprecation") +fun RemoteOperationResult<*>?.toOCFile(): List? = if (this?.isSuccess == true) { + data?.toOCFileList() +} else { + null +} + +private fun ArrayList.toOCFileList(): List = this.mapNotNull { + val remoteFile = (it as? RemoteFile) + + remoteFile?.let { + remoteFile.toOCFile() + } +} + +private fun RemoteFile?.toOCFile(): OCFile = FileStorageUtils.fillOCFile(this) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt new file mode 100644 index 0000000..6db53ba --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -0,0 +1,25 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.nextcloud.model.SearchResultEntryType +import com.owncloud.android.lib.common.SearchResultEntry + +fun SearchResultEntry.getType(): SearchResultEntryType = if (icon == "icon-folder") { + SearchResultEntryType.Folder +} else if (icon.startsWith("icon-note")) { + SearchResultEntryType.Note +} else if (icon.startsWith("icon-contacts")) { + SearchResultEntryType.Contact +} else if (icon.startsWith("icon-calendar")) { + SearchResultEntryType.CalendarEvent +} else if (icon.startsWith("icon-deck")) { + SearchResultEntryType.Deck +} else { + SearchResultEntryType.Unknown +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ShareTypeExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ShareTypeExtensions.kt new file mode 100644 index 0000000..4200fae --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/ShareTypeExtensions.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.lib.resources.shares.ShareType + +fun ShareType.isPublicOrMail(): Boolean = (this == ShareType.PUBLIC_LINK || this == ShareType.EMAIL) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt index 9888e95..b802bac 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt @@ -1,8 +1,8 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions @@ -15,3 +15,40 @@ fun String.getRandomString(length: Int): String { return this + result } + +fun String.removeFileExtension(): String { + val dotIndex = lastIndexOf('.') + return if (dotIndex != -1) { + substring(0, dotIndex) + } else { + this + } +} + +/** + * Checks if two nullable strings are both valid (non-null, non-empty, non-blank) and equal. + * + * It returns `true` only when both strings meet all the following criteria: + * - Neither string is null + * - Neither string is empty ("") + * - Neither string contains only whitespace characters (spaces, tabs, newlines, etc.) + * - Both strings are equal ignoring case differences + * + * @param other The other nullable string to compare with this string + * @return `true` if both strings are valid and equal ignoring case differences, `false` otherwise + */ +fun String?.isNotBlankAndEquals(other: String?): Boolean = this != null && + other != null && + this.isNotBlank() && + other.isNotBlank() && + this.equals(other, ignoreCase = true) + +fun String.truncateWithEllipsis(limit: Int) = take(limit) + if (length > limit) StringConstants.THREE_DOT else "" + +object StringConstants { + const val SLASH = "/" + const val DOT = "." + const val SPACE = " " + const val THREE_DOT = "..." + const val TEMP = "tmp" +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt new file mode 100644 index 0000000..fcdb974 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt @@ -0,0 +1,27 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.datamodel.SyncedFolderDisplayItem +import java.io.File + +fun List.filterEnabledOrWithoutEnabledParent(): List = filter { + it.isEnabled || !hasEnabledParent(it.localPath) +} + +@Suppress("ReturnCount") +fun List.hasEnabledParent(localPath: String?): Boolean { + localPath ?: return false + + val localFile = File(localPath).takeIf { it.exists() } ?: return false + val parent = localFile.parentFile ?: return false + + return any { it.isEnabled && File(it.localPath).exists() && File(it.localPath) == parent } || + hasEnabledParent(parent.absolutePath) +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt index ae54869..11e622f 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ThumbnailsCacheManagerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ThumbnailsCacheManagerExtensions.kt new file mode 100644 index 0000000..962c586 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/ThumbnailsCacheManagerExtensions.kt @@ -0,0 +1,77 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import android.provider.MediaStore +import androidx.exifinterface.media.ExifInterface +import androidx.core.net.toUri +import com.owncloud.android.MainApp +import com.owncloud.android.lib.common.utils.Log_OC + +/** + * Retrieves the orientation of an image file from its EXIF metadata or, as a fallback, + * from the Android MediaStore. + * + * This function first attempts to read the orientation using [ExifInterface.TAG_ORIENTATION] + * directly from the file at the given [path]. If that fails or returns + * [ExifInterface.ORIENTATION_UNDEFINED], it then queries the MediaStore for the image's + * stored orientation in degrees (0, 90, 180, or 270), converting that to an EXIF-compatible + * orientation constant. + * + * @param path Absolute file path or content URI (as string) of the image. + * @return One of the [ExifInterface] orientation constants, e.g. + * [ExifInterface.ORIENTATION_ROTATE_90], or [ExifInterface.ORIENTATION_UNDEFINED] + * if the orientation could not be determined. + * + * @see ExifInterface + * @see MediaStore.Images.Media.ORIENTATION + */ +@Suppress("TooGenericExceptionCaught", "NestedBlockDepth", "MagicNumber") +fun getExifOrientation(path: String): Int { + val context = MainApp.getAppContext() + if (context == null || path.isBlank()) { + return ExifInterface.ORIENTATION_UNDEFINED + } + + var orientation = ExifInterface.ORIENTATION_UNDEFINED + + try { + val exif = ExifInterface(path) + orientation = exif.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_UNDEFINED + ) + } catch (e: Exception) { + Log_OC.e("ThumbnailsCacheManager", "getExifOrientation exception: $e") + } + + // Fallback: query MediaStore if EXIF is undefined + if (orientation == ExifInterface.ORIENTATION_UNDEFINED) { + try { + val uri = path.toUri() + val projection = arrayOf(MediaStore.Images.Media.ORIENTATION) + + context.contentResolver.query(uri, projection, null, null, null)?.use { cursor -> + if (cursor.moveToFirst()) { + val orientationIndex = cursor.getColumnIndexOrThrow(projection[0]) + val degrees = cursor.getInt(orientationIndex) + orientation = when (degrees) { + 90 -> ExifInterface.ORIENTATION_ROTATE_90 + 180 -> ExifInterface.ORIENTATION_ROTATE_180 + 270 -> ExifInterface.ORIENTATION_ROTATE_270 + else -> ExifInterface.ORIENTATION_NORMAL + } + } + } + } catch (e: Exception) { + Log_OC.e("ThumbnailsCacheManager", "getExifOrientation exception: $e") + } + } + + return orientation +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt index 9031271..36195f7 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt @@ -1,20 +1,76 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions +import android.animation.Animator +import android.animation.AnimatorListenerAdapter import android.content.Context import android.graphics.Outline import android.util.TypedValue import android.view.View +import android.view.ViewGroup import android.view.ViewOutlineProvider +import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.nextcloud.ui.behavior.OnScrollBehavior +import com.owncloud.android.lib.common.utils.Log_OC -fun createRoundedOutline(context: Context, cornerRadiusValue: Float): ViewOutlineProvider { - return object : ViewOutlineProvider() { +fun View?.setVisibleIf(condition: Boolean) { + if (this == null) return + visibility = if (condition) View.VISIBLE else View.GONE +} + +fun View?.setVisibilityWithAnimation(condition: Boolean, duration: Long = 200L) { + this ?: return + + if (condition) { + this.apply { + alpha = 0f + visibility = View.VISIBLE + animate() + .alpha(1f) + .setDuration(duration) + .setListener(null) + } + } else { + animate() + .alpha(0f) + .setDuration(duration) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + visibility = View.GONE + } + }) + } +} + +fun View?.makeRounded(context: Context, cornerRadius: Float) { + this?.let { + it.apply { + outlineProvider = createRoundedOutline(context, cornerRadius) + clipToOutline = true + } + } +} + +fun View?.setMargins(left: Int, top: Int, right: Int, bottom: Int) { + if (this == null) { + return + } + + if (layoutParams is ViewGroup.MarginLayoutParams) { + val param = layoutParams as ViewGroup.MarginLayoutParams + param.setMargins(left, top, right, bottom) + requestLayout() + } +} + +fun createRoundedOutline(context: Context, cornerRadiusValue: Float): ViewOutlineProvider = + object : ViewOutlineProvider() { override fun getOutline(view: View, outline: Outline) { val left = 0 val top = 0 @@ -29,4 +85,21 @@ fun createRoundedOutline(context: Context, cornerRadiusValue: Float): ViewOutlin outline.setRoundRect(left, top, right, bottom, cornerRadius.toFloat()) } } + +@Suppress("UNCHECKED_CAST", "ReturnCount", "TooGenericExceptionCaught") +fun T.slideHideBottomBehavior(visible: Boolean) { + this ?: return + val params = layoutParams as? CoordinatorLayout.LayoutParams ?: return + val behavior = params.behavior as? OnScrollBehavior ?: return + post { + try { + if (visible) { + behavior.slideIn(this) + } else { + behavior.slideOut(this) + } + } catch (e: Exception) { + Log_OC.e("slideHideBottomBehavior", e.message) + } + } } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt index efd20b1..9a3a8b6 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.extensions @@ -13,23 +13,24 @@ import com.google.common.util.concurrent.ListenableFuture import com.owncloud.android.lib.common.utils.Log_OC import java.util.concurrent.ExecutionException -fun WorkManager.isWorkScheduled(tag: String): Boolean { - val statuses: ListenableFuture> = this.getWorkInfosByTag(tag) - var running = false +private const val TAG = "WorkManager" + +fun WorkManager.isWorkRunning(tag: String): Boolean = checkWork(tag, listOf(WorkInfo.State.RUNNING)) + +fun WorkManager.isWorkScheduled(tag: String): Boolean = + checkWork(tag, listOf(WorkInfo.State.RUNNING, WorkInfo.State.ENQUEUED)) + +private fun WorkManager.checkWork(tag: String, stateConditions: List): Boolean { + val statuses: ListenableFuture> = getWorkInfosByTag(tag) var workInfoList: List = emptyList() try { workInfoList = statuses.get() } catch (e: ExecutionException) { - Log_OC.d("Worker", "ExecutionException in isWorkScheduled: $e") + Log_OC.d(TAG, "ExecutionException in checkWork: $e") } catch (e: InterruptedException) { - Log_OC.d("Worker", "InterruptedException in isWorkScheduled: $e") + Log_OC.d(TAG, "InterruptedException in checkWork: $e") } - for (workInfo in workInfoList) { - val state = workInfo.state - running = running || (state == WorkInfo.State.RUNNING || state == WorkInfo.State.ENQUEUED) - } - - return running + return workInfoList.any { workInfo -> stateConditions.contains(workInfo.state) } } diff --git a/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt b/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt new file mode 100644 index 0000000..dd1338e --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt @@ -0,0 +1,153 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.fileNameValidator + +import android.content.Context +import android.text.TextUtils +import com.nextcloud.utils.extensions.StringConstants +import com.nextcloud.utils.extensions.checkWCFRestrictions +import com.nextcloud.utils.extensions.forbiddenFilenameBaseNames +import com.nextcloud.utils.extensions.forbiddenFilenameCharacters +import com.nextcloud.utils.extensions.forbiddenFilenameExtensions +import com.nextcloud.utils.extensions.forbiddenFilenames +import com.nextcloud.utils.extensions.removeFileExtension +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.resources.status.OCCapability + +object FileNameValidator { + + /** + * Checks the validity of a file name. + * + * @param filename The name of the file to validate. + * @param capability The capabilities affecting the validation criteria + * such as forbiddenFilenames, forbiddenCharacters. + * @param context The context used for retrieving error messages. + * @param existedFileNames Set of existing file names to avoid duplicates. + * @return An error message if the filename is invalid, null otherwise. + */ + @Suppress("ReturnCount", "NestedBlockDepth") + fun checkFileName( + filename: String, + capability: OCCapability, + context: Context, + existedFileNames: Set? = null + ): String? { + if (filename.isBlank()) { + return context.getString(R.string.filename_empty) + } + + existedFileNames?.let { + if (isFileNameAlreadyExist(filename, existedFileNames)) { + return context.getString(R.string.file_already_exists) + } + } + + if (!capability.checkWCFRestrictions()) { + return null + } + + // region WCF related checks + checkInvalidCharacters(filename, capability, context)?.let { return it } + + val filenameVariants = setOf(filename.lowercase(), filename.removeFileExtension().lowercase()) + + with(capability) { + forbiddenFilenameBaseNamesJson?.let { + forbiddenFilenameBaseNames().find { it.lowercase() in filenameVariants }?.let { forbiddenBaseFilename -> + return context.getString(R.string.file_name_validator_error_reserved_names, forbiddenBaseFilename) + } + } + + forbiddenFilenamesJson?.let { + forbiddenFilenames().find { it.lowercase() in filenameVariants }?.let { forbiddenFilename -> + return context.getString(R.string.file_name_validator_error_reserved_names, forbiddenFilename) + } + } + + forbiddenFilenameExtensionJson?.let { + forbiddenFilenameExtensions().find { extension -> + when { + extension == StringConstants.SPACE -> + filename.startsWith(extension, ignoreCase = true) || + filename.endsWith(extension, ignoreCase = true) + + else -> filename.endsWith(extension, ignoreCase = true) + } + }?.let { forbiddenExtension -> + return if (forbiddenExtension == StringConstants.SPACE) { + context.getString(R.string.file_name_validator_error_forbidden_space_character_extensions) + } else { + context.getString( + R.string.file_name_validator_error_forbidden_file_extensions, + forbiddenExtension + ) + } + } + } + } + // endregion + + return null + } + + /** + * Checks the validity of file paths wanted to move or copied inside the folder. + * + * @param folderPath Target folder to be used for move or copy. + * @param filePaths The list of file paths to move or copy to folderPath. + * @param capability The capabilities affecting the validation criteria. + * @param context The context used for retrieving error messages. + * @return True if folder path and file paths are valid, false otherwise. + */ + fun checkFolderAndFilePaths( + folderPath: String, + filePaths: List, + capability: OCCapability, + context: Context + ): Boolean = checkFolderPath(folderPath, capability, context) && checkFilePaths(filePaths, capability, context) + + fun checkParentRemotePaths(filePaths: List, capability: OCCapability, context: Context): Boolean = + filePaths.all { + if (it.parentRemotePath != StringConstants.SLASH) { + val parentFolderName = it.parentRemotePath.replace(StringConstants.SLASH, "") + checkFileName(parentFolderName, capability, context) == null + } else { + true + } + } + + private fun checkFilePaths(filePaths: List, capability: OCCapability, context: Context): Boolean = + filePaths.all { + checkFileName(it, capability, context) == null + } + + fun checkFolderPath(folderPath: String, capability: OCCapability, context: Context): Boolean = + folderPath.split("[/\\\\]".toRegex()) + .none { it.isNotEmpty() && checkFileName(it, capability, context) != null } + + @Suppress("ReturnCount") + private fun checkInvalidCharacters(name: String, capability: OCCapability, context: Context): String? { + capability.forbiddenFilenameCharactersJson?.let { + val forbiddenFilenameCharacters = capability.forbiddenFilenameCharacters() + + val invalidCharacter = forbiddenFilenameCharacters.firstOrNull { name.contains(it) } + + if (invalidCharacter == null) return null + + return context.getString(R.string.file_name_validator_error_invalid_character, invalidCharacter) + } + + return null + } + + fun isFileHidden(name: String): Boolean = !TextUtils.isEmpty(name) && name[0] == '.' + + fun isFileNameAlreadyExist(name: String, fileNames: Set): Boolean = fileNames.contains(name) +} diff --git a/app/src/main/java/com/nextcloud/utils/mdm/MDMConfig.kt b/app/src/main/java/com/nextcloud/utils/mdm/MDMConfig.kt new file mode 100644 index 0000000..8443636 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/mdm/MDMConfig.kt @@ -0,0 +1,136 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.mdm + +import android.content.Context +import android.content.RestrictionsManager +import com.owncloud.android.R +import com.owncloud.android.utils.appConfig.AppConfigKeys + +object MDMConfig { + fun multiAccountSupport(context: Context): Boolean { + val multiAccountSupport = context.resources.getBoolean(R.bool.multiaccount_support) + + val disableMultiAccountViaMDM = context.getRestriction( + AppConfigKeys.DisableMultiAccount, + context.resources.getBoolean(R.bool.disable_multiaccount) + ) + + return multiAccountSupport && !disableMultiAccountViaMDM + } + + fun shareViaLink(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val shareViaLink = context.resources.getBoolean(R.bool.share_via_link_feature) + + return shareViaLink && !disableShareViaMDM + } + + fun shareViaUser(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val shareViaUsers = context.resources.getBoolean(R.bool.share_with_users_feature) + + return shareViaUsers && !disableShareViaMDM + } + + fun sendFilesSupport(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val sendFilesToOtherApp = "on".equals(context.getString(R.string.send_files_to_other_apps), ignoreCase = true) + + return sendFilesToOtherApp && !disableShareViaMDM + } + + fun sharingSupport(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val sendFilesToOtherApp = "on".equals(context.getString(R.string.send_files_to_other_apps), ignoreCase = true) + + val shareViaUsers = context.resources.getBoolean(R.bool.share_with_users_feature) + + val shareViaLink = context.resources.getBoolean(R.bool.share_via_link_feature) + + return sendFilesToOtherApp && shareViaLink && shareViaUsers && !disableShareViaMDM + } + + fun clipBoardSupport(context: Context): Boolean { + val disableClipboardSupport = context.getRestriction( + AppConfigKeys.DisableClipboard, + context.resources.getBoolean(R.bool.disable_clipboard) + ) + + return !disableClipboardSupport + } + + fun externalSiteSupport(context: Context): Boolean { + val disableMoreExternalSiteViaMDM = context.getRestriction( + AppConfigKeys.DisableMoreExternalSite, + context.resources.getBoolean(R.bool.disable_more_external_site) + ) + + val showExternalLinks = context.resources.getBoolean(R.bool.show_external_links) + + return showExternalLinks && !disableMoreExternalSiteViaMDM + } + + fun showIntro(context: Context): Boolean { + val disableIntroViaMDM = + context.getRestriction(AppConfigKeys.DisableIntro, context.resources.getBoolean(R.bool.disable_intro)) + + val isProviderOrOwnInstallationVisible = context.resources.getBoolean(R.bool.show_provider_or_own_installation) + + return isProviderOrOwnInstallationVisible && !disableIntroViaMDM + } + + fun isLogEnabled(context: Context): Boolean { + val disableLogViaMDM = + context.getRestriction(AppConfigKeys.DisableLog, context.resources.getBoolean(R.bool.disable_log)) + + val loggerEnabled = context.resources.getBoolean(R.bool.logger_enabled) + + return loggerEnabled && !disableLogViaMDM + } + + fun getBaseUrl(context: Context): String = context.getRestriction(AppConfigKeys.BaseUrl, "") + + fun getHost(context: Context): String = + context.getRestriction(AppConfigKeys.ProxyHost, context.getString(R.string.proxy_host)) + + fun getPort(context: Context): Int = + context.getRestriction(AppConfigKeys.ProxyPort, context.resources.getInteger(R.integer.proxy_port)) + + fun enforceProtection(context: Context): Boolean = + context.getRestriction(AppConfigKeys.EnforceProtection, context.resources.getBoolean(R.bool.enforce_protection)) + + @Suppress("UNCHECKED_CAST") + private fun Context.getRestriction(appConfigKey: AppConfigKeys, defaultValue: T): T { + val restrictionsManager = getSystemService(Context.RESTRICTIONS_SERVICE) as? RestrictionsManager + val appRestrictions = restrictionsManager?.getApplicationRestrictions() ?: return defaultValue + + return when (defaultValue) { + is String -> appRestrictions.getString(appConfigKey.key, defaultValue) as T? ?: defaultValue + is Int -> appRestrictions.getInt(appConfigKey.key, defaultValue) as T? ?: defaultValue + is Boolean -> appRestrictions.getBoolean(appConfigKey.key, defaultValue) as T? ?: defaultValue + else -> defaultValue + } + } +} diff --git a/app/src/main/java/com/nextcloud/utils/numberFormatter/NumberFormatter.kt b/app/src/main/java/com/nextcloud/utils/numberFormatter/NumberFormatter.kt new file mode 100644 index 0000000..d036152 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/numberFormatter/NumberFormatter.kt @@ -0,0 +1,21 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.numberFormatter + +import java.text.NumberFormat +import java.util.Locale + +object NumberFormatter { + + @Suppress("MagicNumber") + fun getPercentageText(percent: Int): String { + val formatter = NumberFormat.getPercentInstance(Locale.getDefault()) + formatter.maximumFractionDigits = 0 + return formatter.format(percent / 100.0) + } +} diff --git a/app/src/main/java/com/nextcloud/utils/view/FastScrollPopupBackground.kt b/app/src/main/java/com/nextcloud/utils/view/FastScrollPopupBackground.kt index 430bc99..8d716f6 100644 --- a/app/src/main/java/com/nextcloud/utils/view/FastScrollPopupBackground.kt +++ b/app/src/main/java/com/nextcloud/utils/view/FastScrollPopupBackground.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.view @@ -58,17 +58,11 @@ class FastScrollPopupBackground(context: Context, @ColorInt color: Int) : Drawab // noop } - override fun isAutoMirrored(): Boolean { - return true - } + override fun isAutoMirrored(): Boolean = true - override fun getOpacity(): Int { - return PixelFormat.TRANSPARENT - } + override fun getOpacity(): Int = PixelFormat.TRANSPARENT - private fun shouldMirrorPath(): Boolean { - return DrawableCompat.getLayoutDirection(this) == View.LAYOUT_DIRECTION_RTL - } + private fun shouldMirrorPath(): Boolean = DrawableCompat.getLayoutDirection(this) == View.LAYOUT_DIRECTION_RTL override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean { updatePath() diff --git a/app/src/main/java/com/nextcloud/utils/view/FastScrollUtils.kt b/app/src/main/java/com/nextcloud/utils/view/FastScrollUtils.kt index 34affcf..807d4b3 100644 --- a/app/src/main/java/com/nextcloud/utils/view/FastScrollUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/view/FastScrollUtils.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.utils.view @@ -17,10 +17,7 @@ import javax.inject.Inject class FastScrollUtils @Inject constructor(private val viewThemeUtils: ViewThemeUtils) { @JvmOverloads - fun applyFastScroll( - recyclerView: RecyclerView, - viewHelper: FastScroller.ViewHelper? = null - ) { + fun applyFastScroll(recyclerView: RecyclerView, viewHelper: FastScroller.ViewHelper? = null) { val builder = FastScrollerBuilder(recyclerView).let { viewThemeUtils.files.themeFastScrollerBuilder( diff --git a/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt b/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt index db20687..b39b3f8 100644 --- a/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt +++ b/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt @@ -1,10 +1,10 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Andy Scherzinger - * SPDX-FileCopyrightText: 2023 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2023-2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nmc.android.ui @@ -17,10 +17,13 @@ import android.view.View import androidx.annotation.VisibleForTesting import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.R +import com.owncloud.android.authentication.AuthenticatorActivity import com.owncloud.android.databinding.ActivitySplashBinding import com.owncloud.android.ui.activity.BaseActivity import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.activity.SettingsActivity import javax.inject.Inject class LauncherActivity : BaseActivity() { @@ -64,7 +67,13 @@ class LauncherActivity : BaseActivity() { private fun scheduleSplashScreen() { Handler(Looper.getMainLooper()).postDelayed({ if (user.isPresent) { - startActivity(Intent(this, FileDisplayActivity::class.java)) + if (MDMConfig.enforceProtection(this) && appPreferences.lockPreference == SettingsActivity.LOCK_NONE) { + startActivity(Intent(this, SettingsActivity::class.java)) + } else { + startActivity(Intent(this, FileDisplayActivity::class.java)) + } + } else { + startActivity(Intent(this, AuthenticatorActivity::class.java)) } finish() }, SPLASH_DURATION) diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index f2b4d2e..9358a3c 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 TSI-mc * SPDX-FileCopyrightText: 2022-2023 Álvaro Brey * SPDX-FileCopyrightText: 2016-2020 Tobias Kaminsky @@ -11,7 +11,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2014 David A. Velasco * SPDX-FileCopyrightText: 2013 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android; @@ -21,13 +21,16 @@ import android.app.ActivityManager; import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; +import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.net.ConnectivityManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; @@ -35,6 +38,7 @@ import android.os.StrictMode; import android.text.TextUtils; import android.view.WindowManager; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.nextcloud.appReview.InAppReviewHelper; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; @@ -55,14 +59,18 @@ import com.nextcloud.client.onboarding.OnboardingService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.nextcloud.client.preferences.DarkMode; +import com.nextcloud.receiver.NetworkChangeListener; +import com.nextcloud.receiver.NetworkChangeReceiver; +import com.nextcloud.utils.extensions.ContextExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.nmc.android.ui.LauncherActivity; -import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.authentication.PassCodeManager; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.MediaProvider; +import com.owncloud.android.datamodel.ReceiverFlag; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.ThumbnailsCacheManager; @@ -103,7 +111,6 @@ import javax.net.ssl.SSLEngine; import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDelegate; import androidx.core.util.Pair; import androidx.lifecycle.Lifecycle; @@ -112,8 +119,6 @@ import androidx.lifecycle.ProcessLifecycleOwner; import dagger.android.AndroidInjector; import dagger.android.DispatchingAndroidInjector; import dagger.android.HasAndroidInjector; -import de.cotech.hw.SecurityKeyManager; -import de.cotech.hw.SecurityKeyManagerConfig; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP; @@ -123,9 +128,9 @@ import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFER * Main Application of the project. * Contains methods to build the "static" strings. These strings were before constants in different classes. */ -public class MainApp extends Application implements HasAndroidInjector { - public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_26; - public static final OwnCloudVersion MINIMUM_SUPPORTED_SERVER_VERSION = OwnCloudVersion.nextcloud_16; +public class MainApp extends Application implements HasAndroidInjector, NetworkChangeListener { + public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_29; + public static final OwnCloudVersion MINIMUM_SUPPORTED_SERVER_VERSION = OwnCloudVersion.nextcloud_20; private static final String TAG = MainApp.class.getSimpleName(); public static final String DOT = "."; @@ -137,6 +142,7 @@ public class MainApp extends Application implements HasAndroidInjector { private static boolean mOnlyOnDevice; private static boolean mOnlyPersonalFiles; + @Inject protected AppPreferences preferences; @@ -155,6 +161,9 @@ public class MainApp extends Application implements HasAndroidInjector { @Inject ConnectivityService connectivityService; + @Inject + SyncedFolderProvider syncedFolderProvider; + @Inject PowerManagementService powerManagementService; @Inject @@ -193,6 +202,8 @@ public class MainApp extends Application implements HasAndroidInjector { private static AppComponent appComponent; + private NetworkChangeReceiver networkChangeReceiver; + /** * Temporary hack */ @@ -216,6 +227,11 @@ public class MainApp extends Application implements HasAndroidInjector { return powerManagementService; } + private void registerNetworkChangeReceiver() { + IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(networkChangeReceiver, filter); + } + private String getAppProcessName() { String processName = ""; if(Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { @@ -281,6 +297,7 @@ public class MainApp extends Application implements HasAndroidInjector { return appComponent; } + @SuppressFBWarnings("ST") @Override public void onCreate() { @@ -295,8 +312,6 @@ public class MainApp extends Application implements HasAndroidInjector { insertConscrypt(); - initSecurityKeyManager(); - registerActivityLifecycleCallbacks(new ActivityInjector()); //update the app restart count when app is launched by the user @@ -310,22 +325,24 @@ public class MainApp extends Application implements HasAndroidInjector { fixStoragePath(); + checkCancelDownloadJobs(); + MainApp.storagePath = preferences.getStoragePath(getApplicationContext().getFilesDir().getAbsolutePath()); OwnCloudClientManagerFactory.setUserAgent(getUserAgent()); - try { - OwnCloudClientManagerFactory.setProxyHost(getResources().getString(R.string.proxy_host)); - OwnCloudClientManagerFactory.setProxyPort(getResources().getInteger(R.integer.proxy_port)); - } catch (Resources.NotFoundException e) { - // no proxy set + if (isClientBrandedPlus()) { + setProxyConfig(); + ContextExtensionsKt.registerBroadcastReceiver(this, restrictionsReceiver, restrictionsFilter, ReceiverFlag.NotExported); + } else { + setProxyForNonBrandedPlusClients(); } // initialise thumbnails cache on background thread - new ThumbnailsCacheManager.InitDiskCacheTask().execute(); + ThumbnailsCacheManager.initDiskCacheAsync(); - if (BuildConfig.DEBUG || getApplicationContext().getResources().getBoolean(R.bool.logger_enabled)) { + if (MDMConfig.INSTANCE.isLogEnabled(this)) { // use app writable dir, no permissions needed Log_OC.setLoggerImplementation(new LegacyLoggerAdapter(logger)); Log_OC.d("Debug", "start logging"); @@ -337,8 +354,8 @@ public class MainApp extends Application implements HasAndroidInjector { } catch (Exception e) { Log_OC.d("Debug", "Failed to disable uri exposure"); } - - initSyncOperations(preferences, + initSyncOperations(this, + preferences, uploadsStorageManager, accountManager, connectivityService, @@ -346,16 +363,40 @@ public class MainApp extends Application implements HasAndroidInjector { backgroundJobManager, clock, viewThemeUtils, - walledCheckCache); + walledCheckCache, + syncedFolderProvider); initContactsBackup(accountManager, backgroundJobManager); notificationChannels(); - backgroundJobManager.scheduleMediaFoldersDetectionJob(); - backgroundJobManager.startMediaFoldersDetectionJob(); + if (backgroundJobManager != null) { + backgroundJobManager.scheduleMediaFoldersDetectionJob(); + backgroundJobManager.startMediaFoldersDetectionJob(); + backgroundJobManager.schedulePeriodicHealthStatus(); - backgroundJobManager.schedulePeriodicHealthStatus(); + if (preferences.isTwoWaySyncEnabled()) { + backgroundJobManager.scheduleInternal2WaySync(preferences.getTwoWaySyncInterval()); + } + + backgroundJobManager.startPeriodicallyOfflineOperation(); + } registerGlobalPassCodeProtection(); + networkChangeReceiver = new NetworkChangeReceiver(this, connectivityService); + registerNetworkChangeReceiver(); + + if (!MDMConfig.INSTANCE.sendFilesSupport(this)) { + disableDocumentsStorageProvider(); + } + + + } + + public void disableDocumentsStorageProvider() { + String packageName = getPackageName(); + String providerClassName = "com.owncloud.android.providers.DocumentsStorageProvider"; + ComponentName componentName = new ComponentName(packageName, providerClassName); + PackageManager packageManager = getPackageManager(); + packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { @@ -364,9 +405,61 @@ public class MainApp extends Application implements HasAndroidInjector { } else if (event == Lifecycle.Event.ON_STOP) { passCodeManager.setCanAskPin(true); Log_OC.d(TAG, "APP IN BACKGROUND"); + } else if (event == Lifecycle.Event.ON_RESUME) { + setProxyConfig(); + Log_OC.d(TAG, "APP ON RESUME"); } }); + private void setProxyForNonBrandedPlusClients() { + try { + OwnCloudClientManagerFactory.setProxyHost(getResources().getString(R.string.proxy_host)); + OwnCloudClientManagerFactory.setProxyPort(getResources().getInteger(R.integer.proxy_port)); + } catch (Resources.NotFoundException e) { + Log_OC.d(TAG, "Error caught at setProxyForNonBrandedPlusClients: " + e); + } + } + + public static boolean isClientBranded() { + return getAppContext().getResources().getBoolean(R.bool.is_branded_client); + } + + public static boolean isClientBrandedPlus() { + return getAppContext().getResources().getBoolean(R.bool.is_branded_plus_client); + } + + private final IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); + + private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + setProxyConfig(); + } + }; + + private void setProxyConfig() { + if (!isClientBrandedPlus()) { + Log_OC.d(TAG, "Proxy configuration cannot be set. Client is not branded plus."); + return; + } + + String host = MDMConfig.INSTANCE.getHost(this); + int port = MDMConfig.INSTANCE.getPort(this); + + if (TextUtils.isEmpty(host) || port == -1) { + Log_OC.d(TAG, "Proxy configuration cannot be found"); + return; + } + + try { + OwnCloudClientManagerFactory.setProxyHost(host); + OwnCloudClientManagerFactory.setProxyPort(port); + + Log_OC.d(TAG, "Proxy configuration successfully set"); + } catch (Resources.NotFoundException e) { + Log_OC.e(TAG, "Proxy config cannot able to set due to: $e"); + } + } + private void registerGlobalPassCodeProtection() { registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @@ -418,42 +511,16 @@ public class MainApp extends Application implements HasAndroidInjector { }); } - @SuppressWarnings("unchecked") - private void initSecurityKeyManager() { - SecurityKeyManager securityKeyManager = SecurityKeyManager.getInstance(); - final SecurityKeyManagerConfig.Builder configBuilder = new SecurityKeyManagerConfig.Builder() - .setEnableDebugLogging(BuildConfig.DEBUG); - - try { - // exclude all activities except AuthenticatorActivity - final PackageManager pm = this.getPackageManager(); - final PackageInfo info = pm.getPackageInfo(this.getPackageName(), PackageManager.GET_ACTIVITIES); - final ActivityInfo[] activities = info.activities; - for (ActivityInfo activityInfo : activities) { - try { - final Class aClass = (Class) Class.forName(activityInfo.name); - if (aClass != AuthenticatorActivity.class) { - configBuilder.addExcludedActivityClass(aClass); - } - } catch (ClassNotFoundException | ClassCastException e) { - Log_OC.e(TAG, "Couldn't disable activity for security key listener", e); - } - } - } catch (PackageManager.NameNotFoundException e) { - Log_OC.e(TAG, "Couldn't disable activities for security key listener", e); - } - - - securityKeyManager.init(this, configBuilder.build()); - } - public static void initContactsBackup(UserAccountManager accountManager, BackgroundJobManager backgroundJobManager) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(appContext.get()); + if (accountManager == null) { + return; + } + List users = accountManager.getAllUsers(); for (User user : users) { - if (arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP)) { + if (backgroundJobManager != null && arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP)) { backgroundJobManager.schedulePeriodicContactsBackup(user); - } } } @@ -506,7 +573,6 @@ public class MainApp extends Application implements HasAndroidInjector { if (storagePoint.getPrivacyType() == StoragePoint.PrivacyType.PUBLIC) { preferences.setStoragePath(storagePoint.getPath()); preferences.removeKeysMigrationPreference(); - set = true; break; } } @@ -538,7 +604,15 @@ public class MainApp extends Application implements HasAndroidInjector { } } + private void checkCancelDownloadJobs() { + if (backgroundJobManager != null && preferences.shouldStopDownloadJobsOnStart()) { + backgroundJobManager.cancelAllFilesDownloadJobs(); + preferences.setStopDownloadJobsOnStart(false); + } + } + public static void initSyncOperations( + final Context context, final AppPreferences preferences, final UploadsStorageManager uploadsStorageManager, final UserAccountManager accountManager, @@ -547,8 +621,9 @@ public class MainApp extends Application implements HasAndroidInjector { final BackgroundJobManager backgroundJobManager, final Clock clock, final ViewThemeUtils viewThemeUtils, - final WalledCheckCache walledCheckCache) { - updateToAutoUpload(); + final WalledCheckCache walledCheckCache, + final SyncedFolderProvider syncedFolderProvider) { + updateToAutoUpload(context); cleanOldEntries(clock); updateAutoUploadEntries(clock); @@ -561,12 +636,12 @@ public class MainApp extends Application implements HasAndroidInjector { } if (!preferences.isAutoUploadInitialized()) { - backgroundJobManager.startImmediateFilesSyncJob(false, new String[]{}); + FilesSyncHelper.startFilesSyncForAllFolders(syncedFolderProvider, backgroundJobManager,false, new String[]{}); preferences.setAutoUploadInit(true); } - FilesSyncHelper.scheduleFilesSyncIfNeeded(appContext.get(), backgroundJobManager); - FilesSyncHelper.restartJobsIfNeeded( + FilesSyncHelper.scheduleFilesSyncForAllFoldersIfNeeded(appContext.get(), syncedFolderProvider, backgroundJobManager); + FilesSyncHelper.restartUploadsIfNeeded( uploadsStorageManager, accountManager, connectivityService, @@ -592,7 +667,7 @@ public class MainApp extends Application implements HasAndroidInjector { } public static void notificationChannels() { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O && getAppContext() != null) { + if (getAppContext() != null) { Context context = getAppContext(); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); @@ -604,7 +679,7 @@ public class MainApp extends Application implements HasAndroidInjector { createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD, R.string.notification_channel_upload_name_short, - R.string.notification_channel_upload_description, context); + R.string.notification_channel_upload_description, context, NotificationManager.IMPORTANCE_LOW); createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_MEDIA, R.string.notification_channel_media_name, @@ -620,9 +695,17 @@ public class MainApp extends Application implements HasAndroidInjector { R.string.notification_channel_push_name, R.string .notification_channel_push_description, context, NotificationManager.IMPORTANCE_DEFAULT); + createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_BACKGROUND_OPERATIONS, + R.string.notification_channel_background_operations_name, R.string + .notification_channel_background_operations_description, context, NotificationManager.IMPORTANCE_LOW); + createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_GENERAL, R.string .notification_channel_general_name, R.string.notification_channel_general_description, context, NotificationManager.IMPORTANCE_DEFAULT); + + createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_OFFLINE_OPERATIONS, + R.string.notification_channel_offline_operations_name_short, + R.string.notification_channel_offline_operations_description, context); } else { Log_OC.e(TAG, "Notification manager is null"); } @@ -639,8 +722,7 @@ public class MainApp extends Application implements HasAndroidInjector { private static void createChannel(NotificationManager notificationManager, String channelId, int channelName, int channelDescription, Context context, int importance) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O - && getAppContext() != null) { + if (getAppContext() != null) { CharSequence name = context.getString(channelName); String description = context.getString(channelDescription); NotificationChannel channel = new NotificationChannel(channelId, name, importance); @@ -735,6 +817,10 @@ public class MainApp extends Application implements HasAndroidInjector { return getUserAgent(R.string.nextcloud_user_agent); } + public static void showMessage(int messageId) { + ContextExtensionsKt.showToast(getAppContext(), messageId); + } + // user agent private static String getUserAgent(@StringRes int agent) { String appString = string(agent); @@ -754,32 +840,37 @@ public class MainApp extends Application implements HasAndroidInjector { return String.format(appString, version, brandedName); } - private static void updateToAutoUpload() { - Context context = getAppContext(); + private static void updateToAutoUpload(Context context) { AppPreferences preferences = AppPreferencesImpl.fromContext(context); if (preferences.instantPictureUploadEnabled() || preferences.instantVideoUploadEnabled()) { preferences.removeLegacyPreferences(); // show info pop-up try { - new AlertDialog.Builder(context, R.style.Theme_ownCloud_Dialog) - .setTitle(R.string.drawer_synced_folders) - .setMessage(R.string.synced_folders_new_info) - .setPositiveButton(R.string.drawer_open, (dialog, which) -> { - // show Auto Upload - Intent folderSyncIntent = new Intent(context, SyncedFoldersActivity.class); - dialog.dismiss(); - context.startActivity(folderSyncIntent); - }) - .setNegativeButton(R.string.drawer_close, (dialog, which) -> dialog.dismiss()) - .setIcon(R.drawable.nav_synced_folders) - .show(); + showAutoUploadAlertDialog(context); } catch (WindowManager.BadTokenException e) { Log_OC.i(TAG, "Error showing Auto Upload Update dialog, so skipping it: " + e.getMessage()); } } } + + + private static void showAutoUploadAlertDialog(Context context) { + new MaterialAlertDialogBuilder(context, R.style.Theme_ownCloud_Dialog) + .setTitle(R.string.drawer_synced_folders) + .setMessage(R.string.synced_folders_new_info) + .setPositiveButton(R.string.drawer_open, (dialog, which) -> { + Intent folderSyncIntent = new Intent(context, SyncedFoldersActivity.class); + dialog.dismiss(); + context.startActivity(folderSyncIntent); + }) + .setNegativeButton(R.string.drawer_close, (dialog, which) -> dialog.dismiss()) + .setIcon(R.drawable.nav_synced_folders) + .create() + .show(); + } + private static void updateAutoUploadEntries(Clock clock) { // updates entries to reflect their true paths Context context = getAppContext(); @@ -917,4 +1008,22 @@ public class MainApp extends Application implements HasAndroidInjector { case SYSTEM -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); } } + + @Override + public void networkAndServerConnectionListener(boolean isNetworkAndServerAvailable) { + if (backgroundJobManager == null) { + Log_OC.d(TAG, "Offline operations terminated, backgroundJobManager cannot be null"); + return; + } + + if (isNetworkAndServerAvailable) { + backgroundJobManager.startOfflineOperations(); + } + } + + @Override + public void onTerminate() { + super.onTerminate(); + ReceiversHelper.shutdown(); + } } diff --git a/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java b/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java index e7a5572..6f3adf6 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java +++ b/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2012 David A. Velasco * SPDX-FileCopyrightText: 2011-2012 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.authentication; @@ -21,6 +21,7 @@ import android.os.Bundle; import android.os.Handler; import android.widget.Toast; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.lib.common.accounts.AccountTypeUtils; @@ -70,7 +71,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { final Bundle bundle = new Bundle(); - if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) { + if (accounts.length < 1 || MDMConfig.INSTANCE.multiAccountSupport(mContext)) { try { validateAccountType(accountType); } catch (AuthenticatorException e) { diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthObject.kt b/app/src/main/java/com/owncloud/android/authentication/AuthObject.kt new file mode 100644 index 0000000..eddaecc --- /dev/null +++ b/app/src/main/java/com/owncloud/android/authentication/AuthObject.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.authentication + +data class AuthObject(val poll: Poll, val login: String) + +data class Poll(val token: String, val endpoint: String) diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index f285b2a..c87c015 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 TSI-mc + * SPDX-FileCopyrightText: 2023-2025 TSI-mc * SPDX-FileCopyrightText: 2019-2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Andy Scherzinger * SPDX-FileCopyrightText: 2017 Mario Danic @@ -9,7 +9,7 @@ * SPDX-FileCopyrightText: 2013-2015 María Asensio Valverde * SPDX-FileCopyrightText: 2013-2015 David A. Velasco * SPDX-FileCopyrightText: 2011-2012 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.authentication; @@ -17,58 +17,67 @@ import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.Color; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.preference.PreferenceManager; import android.text.TextUtils; -import android.util.AndroidRuntimeException; +import android.util.Pair; import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.EditorInfo; import android.webkit.CookieManager; -import android.webkit.CookieSyncManager; +import android.webkit.URLUtil; +import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; -import android.webkit.WebResourceResponse; import android.webkit.WebView; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; import com.blikoon.qrcodescanner.QrCodeActivity; -import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.button.MaterialButton; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; import com.nextcloud.android.common.ui.color.ColorUtil; import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.device.DeviceInfo; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.client.onboarding.OnboardingService; import com.nextcloud.client.preferences.AppPreferences; +import com.nextcloud.common.PlainClient; +import com.nextcloud.operations.PostMethod; import com.nextcloud.utils.extensions.BundleExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; +import com.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.AccountSetupBinding; import com.owncloud.android.databinding.AccountSetupWebviewBinding; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.OwnCloudCredentials; import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.UserInfo; +import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; import com.owncloud.android.lib.common.network.CertificateCombinedException; @@ -77,9 +86,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.status.GetCapabilitiesRemoteOperation; -import com.owncloud.android.lib.resources.status.NextcloudVersion; -import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod; @@ -90,6 +96,7 @@ import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.ui.NextcloudWebViewClient; import com.owncloud.android.ui.activity.FileDisplayActivity; +import com.owncloud.android.ui.activity.SettingsActivity; import com.owncloud.android.ui.dialog.IndeterminateProgressDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener; @@ -97,34 +104,42 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.WebViewUtil; -import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import java.io.InputStream; import java.net.URLDecoder; +import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; -import androidx.annotation.ColorInt; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.ActionBar; -import androidx.core.content.ContextCompat; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import de.cotech.hw.fido.WebViewFidoBridge; -import de.cotech.hw.fido.ui.FidoDialogOptions; -import de.cotech.hw.fido2.WebViewWebauthnBridge; -import de.cotech.hw.fido2.ui.WebauthnDialogOptions; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.ProcessLifecycleOwner; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import okhttp3.FormBody; +import okhttp3.RequestBody; import static com.owncloud.android.utils.PermissionUtil.PERMISSIONS_CAMERA; @@ -162,7 +177,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private static final String KEY_USERNAME = "USERNAME"; private static final String KEY_PASSWORD = "PASSWORD"; private static final String KEY_ASYNC_TASK_IN_PROGRESS = "AUTH_IN_PROGRESS"; - public static final String WEB_LOGIN = "/index.php/login/flow"; + + public static final String WEB_LOGIN = "/index.php/login/v2"; + public static final String PROTOCOL_SUFFIX = "://"; public static final String LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":"; public static final String HTTPS_PROTOCOL = "https://"; @@ -170,8 +187,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity public static final int NO_ICON = 0; public static final String EMPTY_STRING = ""; - - private static final int REQUEST_CODE_QR_SCAN = 101; public static final int REQUEST_CODE_FIRST_RUN = 102; /// parameters from EXTRAs in starter Intent @@ -194,9 +209,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private GetServerInfoOperation.ServerInfo mServerInfo = new GetServerInfoOperation.ServerInfo(); /// Authentication PRE-Fragment elements - private WebViewFidoBridge webViewFidoU2fBridge; - private WebViewWebauthnBridge webViewWebauthnBridge; - private String mAuthStatusText = EMPTY_STRING; private int mAuthStatusIcon; @@ -218,13 +230,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity @Inject PassCodeManager passCodeManager; @Inject ViewThemeUtils.Factory viewThemeUtilsFactory; @Inject ColorUtil colorUtil; + @Inject ClientFactory clientFactory; + private AuthObject authObject = null; + private String fallbackToken; private boolean onlyAdd = false; - @SuppressLint("ResourceAsColor") @ColorInt - private int primaryColor = R.color.primary; - private boolean strictMode = false; + + private final Gson gson = new Gson(); private ViewThemeUtils viewThemeUtils; + private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); @VisibleForTesting public AccountSetupBinding getAccountSetupBinding() { @@ -240,9 +255,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewThemeUtils = viewThemeUtilsFactory.withPrimaryAsBackground(); - viewThemeUtils.platform.themeStatusBar(this, ColorRole.PRIMARY); - - WebViewUtil webViewUtil = new WebViewUtil(this); + viewThemeUtils.platform.colorStatusBar(this, getResources().getColor(R.color.primary)); Uri data = getIntent().getData(); boolean directLogin = data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme)); @@ -284,13 +297,24 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG); } + boolean webViewLoginMethod = false; String webloginUrl = null; - boolean webViewLoginMethod; - if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { + + if (MainApp.isClientBrandedPlus()) { + String baseUrl = MDMConfig.INSTANCE.getBaseUrl(this); + if (!TextUtils.isEmpty(baseUrl)) { + webloginUrl = baseUrl + WEB_LOGIN; + } + } + + if (!TextUtils.isEmpty(webloginUrl)) { + webViewLoginMethod = true; + } else if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { webViewLoginMethod = true; webloginUrl = getString(R.string.provider_registration_server); - } else { - webViewLoginMethod = !TextUtils.isEmpty(getResources().getString(R.string.webview_login_url)); + } else if (!TextUtils.isEmpty(getResources().getString(R.string.webview_login_url))) { + webViewLoginMethod = true; + webloginUrl = getResources().getString(R.string.webview_login_url); showWebViewLoginUrl = getResources().getBoolean(R.bool.show_server_url_input); } @@ -298,7 +322,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity if (webViewLoginMethod) { accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); - initWebViewLogin(webloginUrl, false); + anonymouslyPostLoginRequest(webloginUrl); } else { accountSetupBinding = AccountSetupBinding.inflate(getLayoutInflater()); setContentView(accountSetupBinding.getRoot()); @@ -309,80 +333,256 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity /// initialize block to be moved to single Fragment to check server and get info about it /// initialize block to be moved to single Fragment to retrieve and validate credentials - initAuthorizationPreFragment(savedInstanceState); + if (TextUtils.isEmpty(getString(R.string.enforce_servers))) { + initAuthorizationPreFragment(savedInstanceState); + } else { + showEnforcedServers(); + } + + initServerPreFragment(savedInstanceState); } - initServerPreFragment(savedInstanceState); + ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver); + } - webViewUtil.checkWebViewVersion(); + private void showEnforcedServers() { + showAuthStatus(); + accountSetupBinding.hostUrlFrame.setVisibility(View.GONE); + accountSetupBinding.hostUrlInputHelperText.setVisibility(View.GONE); + accountSetupBinding.scanQr.setVisibility(View.GONE); + accountSetupBinding.serversSpinner.setVisibility(View.VISIBLE); + + ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.enforced_servers_spinner); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + ArrayList servers = new ArrayList<>(); + servers.add(""); + adapter.add(getString(R.string.please_select_a_server)); + + ArrayList t = new Gson().fromJson(getString(R.string.enforce_servers), + new TypeToken>() { + } + .getType()); + + for (EnforcedServer e : t) { + adapter.add(e.getName()); + servers.add(e.getUrl()); + } + + accountSetupBinding.serversSpinner.setAdapter(adapter); + accountSetupBinding.serversSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + String url = servers.get(position); + + if (URLUtil.isValidUrl(url)) { + accountSetupBinding.hostUrlInput.setText(url); + checkOcServer(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // do nothing + } + }); } private void deleteCookies() { try { - CookieSyncManager.createInstance(this); CookieManager.getInstance().removeAllCookies(null); - } catch (AndroidRuntimeException e) { + } catch (Exception e) { Log_OC.e(TAG, e.getMessage()); } } - private static String getWebLoginUserAgent() { - return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + - Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL + " (Android)"; + // region LoginFlow + private final ScheduledExecutorService loginFlowExecutorService = Executors.newSingleThreadScheduledExecutor(); + private boolean isLoginProcessCompleted = false; + private boolean isRedirectedToTheDefaultBrowser = false; + private String baseUrl; + + private void poolLogin() { + loginFlowExecutorService.scheduleWithFixedDelay(() -> { + if (!isLoginProcessCompleted) { + performLoginFlowV2(); + } + }, 0, 30, TimeUnit.SECONDS); } - @SuppressFBWarnings("ANDROID_WEB_VIEW_JAVASCRIPT") - @SuppressLint("SetJavaScriptEnabled") - private void initWebViewLogin(String baseURL, boolean useGenericUserAgent) { - viewThemeUtils.platform.colorCircularProgressBar(accountSetupWebviewBinding.loginWebviewProgressBar, ColorRole.ON_PRIMARY_CONTAINER); - accountSetupWebviewBinding.loginWebview.setVisibility(View.GONE); - new WebViewUtil(this).setProxyKKPlus(accountSetupWebviewBinding.loginWebview); - - accountSetupWebviewBinding.loginWebview.getSettings().setAllowFileAccess(false); - accountSetupWebviewBinding.loginWebview.getSettings().setJavaScriptEnabled(true); - accountSetupWebviewBinding.loginWebview.getSettings().setDomStorageEnabled(true); - - if (useGenericUserAgent) { - accountSetupWebviewBinding.loginWebview.getSettings().setUserAgentString(MainApp.getUserAgent()); - } else { - accountSetupWebviewBinding.loginWebview.getSettings().setUserAgentString(getWebLoginUserAgent()); + /** + * This function facilitates the login process by anonymously posting a login request to a specified URL. + * After posting the request, it retrieves the login URL for completing the login flow. + * The login flow version used is v2. + * + * @param url The URL where the login request is to be anonymously posted. + * This URL should handle the login request and return the login URL. + * It's typically the entry point for the login process. + * Example: "..." + */ + private void anonymouslyPostLoginRequest(String url) { + if (TextUtils.isEmpty(url)) { + DisplayUtils.showSnackMessage(this, R.string.authenticator_activity_empty_base_url); + return; } - accountSetupWebviewBinding.loginWebview.getSettings().setSaveFormData(false); - accountSetupWebviewBinding.loginWebview.getSettings().setSavePassword(false); + baseUrl = url; - FidoDialogOptions.Builder dialogOptionsBuilder = FidoDialogOptions.builder(); - dialogOptionsBuilder.setShowSdkLogo(true); - dialogOptionsBuilder.setTheme(R.style.FidoDialog); - webViewFidoU2fBridge = WebViewFidoBridge.createInstanceForWebView( - this, accountSetupWebviewBinding.loginWebview, dialogOptionsBuilder); + singleThreadExecutor.execute(() -> { + String response = getResponseOfAnonymouslyPostLoginRequest(); + if (TextUtils.isEmpty(response)) { + DisplayUtils.showSnackMessage(AuthenticatorActivity.this, R.string.authenticator_activity_empty_response_message); + return; + } - WebauthnDialogOptions.Builder webauthnOptionsBuilder = WebauthnDialogOptions.builder(); - webauthnOptionsBuilder.setShowSdkLogo(true); - webauthnOptionsBuilder.setAllowSkipPin(true); - webauthnOptionsBuilder.setTheme(R.style.FidoDialog); - webViewWebauthnBridge = WebViewWebauthnBridge.createInstanceForWebView( - this, accountSetupWebviewBinding.loginWebview, webauthnOptionsBuilder); - - Map headers = new HashMap<>(); - headers.put(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE); - - String url; - if (baseURL != null && !baseURL.isEmpty()) { - url = baseURL; - } else { - url = getResources().getString(R.string.webview_login_url); - } - - new WebViewUtil(this).setProxyKKPlus(accountSetupWebviewBinding.loginWebview); - if (url.startsWith(HTTPS_PROTOCOL)) { - strictMode = true; - } - - accountSetupWebviewBinding.loginWebview.loadUrl(url, headers); - - setClient(); + String loginUrl = extractLoginUrl(response); + runOnUiThread(() -> { + initLoginInfoView(); + launchDefaultWebBrowser(loginUrl); + }); + }); } + private String extractLoginUrl(String response) { + try { + authObject = gson.fromJson(response, AuthObject.class); + if (authObject != null && !TextUtils.isEmpty(authObject.getLogin())) { + return authObject.getLogin(); + } else { + Log_OC.e(TAG, "AuthObject parsing failed or login empty, trying JSONObject fallback"); + } + } catch (Exception e) { + Log_OC.e(TAG, "Error parsing AuthObject: " + e.getMessage(), e); + } + + try { + String fallbackUrl = getLoginFromJsonObject(response); + if (!TextUtils.isEmpty(fallbackUrl)) { + return fallbackUrl; + } else { + Log_OC.e(TAG, "Fallback JSONObject parsing failed or login empty"); + } + } catch (Exception e) { + Log_OC.e(TAG, "Error parsing fallback JSONObject: " + e.getMessage(), e); + } + + Log_OC.e(TAG, "Both AuthObject and fallback parsing failed, returning default login URL"); + DisplayUtils.showSnackMessage(this, R.string.authenticator_activity_login_error); + return getResources().getString(R.string.webview_login_url); + } + + private String getLoginFromJsonObject(String response) { + JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); + fallbackToken = jsonObject.getAsJsonObject("poll").get("token").getAsString(); + return jsonObject.get("login").getAsString(); + } + + private String getResponseOfAnonymouslyPostLoginRequest() { + PostMethod post = new PostMethod(baseUrl, false, new FormBody.Builder().build()); + PlainClient client = clientFactory.createPlainClient(); + post.execute(client); + return post.getResponseBodyAsString(); + } + + private void launchDefaultWebBrowser(String url) { + if (url == null || url.isBlank()) { + DisplayUtils.showSnackMessage(this, R.string.invalid_url); + return; + } + + try { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PackageManager packageManager = getPackageManager(); + + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent); + } else { + DisplayUtils.showSnackMessage(this, R.string.authenticator_activity_no_web_browser_found); + } + } catch (Exception e) { + Log_OC.e(TAG, "Exception launchDefaultWebBrowser: " + e); + DisplayUtils.showSnackMessage(this, R.string.authenticator_activity_login_error); + } + } + + private Pair extractPollUrlAndToken() { + if (authObject != null) { + final var poll = authObject.getPoll(); + String pollUrl = poll.getEndpoint(); + String token = poll.getToken(); + + if (TextUtils.isEmpty(pollUrl)) { + Log_OC.e(TAG, "auth object poll url is empty."); + } + if (TextUtils.isEmpty(token)) { + Log_OC.e(TAG, "auth object token is empty."); + } + + if (!TextUtils.isEmpty(pollUrl) && !TextUtils.isEmpty(token)) { + return new Pair<>(pollUrl, token); + } + } + + return new Pair<>(baseUrl + "/poll", fallbackToken); + } + + private void performLoginFlowV2() { + final var pollUrlAndToken = extractPollUrlAndToken(); + + RequestBody requestBody = new FormBody.Builder() + .add("token", pollUrlAndToken.second) + .build(); + + PlainClient client = clientFactory.createPlainClient(); + PostMethod post = new PostMethod(pollUrlAndToken.first, false, requestBody); + int status = post.execute(client); + String response = post.getResponseBodyAsString(); + + Log_OC.d(TAG, "performLoginFlowV2 status: " + status); + Log_OC.d(TAG, "performLoginFlowV2 response: " + response); + + if (!response.isEmpty()) { + runOnUiThread(() -> completeLoginFlow(response, status)); + } + } + + private void completeLoginFlow(String response, int status) { + try { + LoginUrlInfo loginUrlInfo = gson.fromJson(response, LoginUrlInfo.class); + if (loginUrlInfo == null) { + Log_OC.e(TAG, "cannot complete login flow loginUrl is null"); + return; + } + isLoginProcessCompleted = loginUrlInfo.isValid(status); + + if (accountSetupBinding != null) { + accountSetupBinding.hostUrlInput.setText(""); + } + + mServerInfo.mBaseUrl = AuthenticatorUrlUtils.INSTANCE.normalizeUrlSuffix(loginUrlInfo.getServer()); + webViewUser = loginUrlInfo.getLoginName(); + webViewPassword = loginUrlInfo.getAppPassword(); + } catch (Exception e) { + Log_OC.d(TAG, "Error completeLoginFlow: " + e); + mServerStatusIcon = R.drawable.ic_alert; + mServerStatusText = getString(R.string.qr_could_not_be_read); + showServerStatus(); + } + + checkOcServer(); + loginFlowExecutorService.shutdown(); + ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); + } + + private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { + if (event == Lifecycle.Event.ON_START && authObject != null && !TextUtils.isEmpty(authObject.getPoll().getToken())) { + Log_OC.d(TAG, "Start poolLogin"); + poolLogin(); + } + }); + // endregion + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (accountSetupWebviewBinding != null && event.getAction() == KeyEvent.ACTION_DOWN && @@ -400,29 +600,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private void setClient() { accountSetupWebviewBinding.loginWebview.setWebViewClient(new NextcloudWebViewClient(getSupportFragmentManager()) { @Override - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - webViewFidoU2fBridge.delegateShouldInterceptRequest(view, request); - webViewWebauthnBridge.delegateShouldInterceptRequest(view, request); - return super.shouldInterceptRequest(view, request); - } - - @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - super.onPageStarted(view, url, favicon); - webViewFidoU2fBridge.delegateOnPageStarted(view, url, favicon); - webViewWebauthnBridge.delegateOnPageStarted(view, url, favicon); - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + String url = request.getUrl().toString(); if (url.startsWith(getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/")) { parseAndLoginFromWebView(url); return true; } - if (strictMode && url.startsWith(HTTP_PROTOCOL)) { - Snackbar.make(view, R.string.strict_mode, Snackbar.LENGTH_LONG).show(); - return true; - } return false; } @@ -432,18 +615,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity accountSetupWebviewBinding.loginWebviewProgressBar.setVisibility(View.GONE); accountSetupWebviewBinding.loginWebview.setVisibility(View.VISIBLE); - - if (mServerInfo.mVersion != null && mServerInfo.mVersion.isOlderThan(NextcloudVersion.nextcloud_25)) { - viewThemeUtils.platform.colorStatusBar(AuthenticatorActivity.this, primaryColor); - getWindow().setNavigationBarColor(primaryColor); - } else { - viewThemeUtils.platform.resetStatusBar(AuthenticatorActivity.this); - getWindow().setNavigationBarColor(ContextCompat.getColor(AuthenticatorActivity.this, R.color.bg_default)); - } } @Override - public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { accountSetupWebviewBinding.loginWebviewProgressBar.setVisibility(View.GONE); accountSetupWebviewBinding.loginWebview.setVisibility(View.VISIBLE); @@ -465,9 +640,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity if (accountSetupBinding != null) { accountSetupBinding.hostUrlInput.setText(""); } - mServerInfo.mBaseUrl = AuthenticatorUrlUtils.INSTANCE.normalizeUrlSuffix(loginUrlInfo.serverAddress); - webViewUser = loginUrlInfo.username; - webViewPassword = loginUrlInfo.password; + mServerInfo.mBaseUrl = AuthenticatorUrlUtils.INSTANCE.normalizeUrlSuffix(loginUrlInfo.getServer()); + webViewUser = loginUrlInfo.getLoginName(); + webViewPassword = loginUrlInfo.getAppPassword(); } catch (Exception e) { mServerStatusIcon = R.drawable.ic_alert; mServerStatusText = getString(R.string.qr_could_not_be_read); @@ -500,18 +675,18 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity throw new IllegalArgumentException("Illegal number of login URL elements detected: " + values.length); } - LoginUrlInfo loginUrlInfo = new LoginUrlInfo(); + LoginUrlInfo loginUrlInfo = new LoginUrlInfo("", "", ""); for (String value : values) { if (value.startsWith("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginUrlInfo.username = URLDecoder.decode( - value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); + loginUrlInfo.setLoginName(URLDecoder.decode( + value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); } else if (value.startsWith("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginUrlInfo.password = URLDecoder.decode( - value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); + loginUrlInfo.setAppPassword(URLDecoder.decode( + value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); } else if (value.startsWith("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginUrlInfo.serverAddress = URLDecoder.decode( - value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); + loginUrlInfo.setServer(URLDecoder.decode( + value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); } } @@ -539,6 +714,22 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } else { accountSetupBinding.scanQr.setVisibility(View.GONE); } + + addDebugLogin(); + } + + private void addDebugLogin() { + if (BuildConfig.DEBUG) { + try { + accountSetupBinding.thumbnail.setOnLongClickListener(v -> { + final String dataString = BuildConfig.NC_TEST_SERVER_DATA_STRING; + parseAndLoginFromWebView(dataString); + return false; + }); + } catch (Throwable t) { + Log_OC.w(TAG, "Test server data string not available in this build"); + } + } } /** @@ -665,9 +856,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity passCodeManager.onActivityResumed(this); Uri data = intent.getData(); - if (data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme))) { - if (!getResources().getBoolean(R.bool.multiaccount_support) && + if (!MDMConfig.INSTANCE.multiAccountSupport(this) && accountManager.getAccounts().length == 1) { Toast.makeText(this, R.string.no_mutliple_accounts_allowed, Toast.LENGTH_LONG).show(); finish(); @@ -680,10 +870,36 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity if (intent.getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); - initWebViewLogin(getString(R.string.provider_registration_server), true); + initSimpleSignupLogin(); } } + @SuppressFBWarnings("ANDROID_WEB_VIEW_JAVASCRIPT") + @SuppressLint("SetJavaScriptEnabled") + private void initSimpleSignupLogin() { + viewThemeUtils.platform.colorCircularProgressBar(accountSetupWebviewBinding.loginWebviewProgressBar, ColorRole.ON_PRIMARY_CONTAINER); + accountSetupWebviewBinding.loginWebview.setVisibility(View.GONE); + new WebViewUtil().setProxyKKPlus(accountSetupWebviewBinding.loginWebview); + + accountSetupWebviewBinding.loginWebview.getSettings().setAllowFileAccess(false); + accountSetupWebviewBinding.loginWebview.getSettings().setJavaScriptEnabled(true); + accountSetupWebviewBinding.loginWebview.getSettings().setDomStorageEnabled(true); + + accountSetupWebviewBinding.loginWebview.getSettings().setUserAgentString(MainApp.getUserAgent()); + accountSetupWebviewBinding.loginWebview.getSettings().setSaveFormData(false); + accountSetupWebviewBinding.loginWebview.getSettings().setSavePassword(false); + + Map headers = new HashMap<>(); + headers.put(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE); + + new WebViewUtil().setProxyKKPlus(accountSetupWebviewBinding.loginWebview); + + accountSetupWebviewBinding.loginWebview.loadUrl(getString(R.string.provider_registration_server), headers); + accountSetupWebviewBinding.loginFlowV2.loginFlowInfoV2.setVisibility(View.GONE); + + setClient(); + } + private boolean checkIfViaSSO(Intent intent) { Bundle extras = intent.getExtras(); if (extras == null) { @@ -734,6 +950,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mOperationsServiceBinder = null; } + Log_OC.d(TAG, "AuthenticatorActivity onDestroy called"); + singleThreadExecutor.shutdown(); super.onDestroy(); } @@ -752,7 +970,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mServerInfo = new GetServerInfoOperation.ServerInfo(); - if (uri.length() != 0) { + if (!uri.isEmpty()) { if (accountSetupBinding != null) { uri = AuthenticatorUrlUtils.INSTANCE.stripIndexPhpOrAppsFiles(uri); accountSetupBinding.hostUrlInput.setText(uri); @@ -884,38 +1102,21 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity // 4. we got the authentication method required by the server mServerInfo = (GetServerInfoOperation.ServerInfo) (result.getData().get(0)); - // show outdated warning - if (CapabilityUtils.checkOutdatedWarning(getResources(), - mServerInfo.mVersion, - mServerInfo.hasExtendedSupport)) { - DisplayUtils.showServerOutdatedSnackbar(this, Snackbar.LENGTH_INDEFINITE); - } - if (webViewUser != null && !webViewUser.isEmpty() && webViewPassword != null && !webViewPassword.isEmpty()) { checkBasicAuthorization(webViewUser, webViewPassword); } else { - new Thread(() -> { - OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mServerInfo.mBaseUrl), - this, - true); - RemoteOperationResult remoteOperationResult = new GetCapabilitiesRemoteOperation().execute(client); - - if (remoteOperationResult.isSuccess() && - remoteOperationResult.getData() != null && - remoteOperationResult.getData().size() > 0) { - OCCapability capability = (OCCapability) remoteOperationResult.getData().get(0); - try { - primaryColor = Color.parseColor(capability.getServerColor()); - } catch (Exception e) { - // falls back to primary color - } - } - }).start(); - accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); - initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false); + + if (!isLoginProcessCompleted) { + if (!isRedirectedToTheDefaultBrowser) { + anonymouslyPostLoginRequest(mServerInfo.mBaseUrl + WEB_LOGIN); + isRedirectedToTheDefaultBrowser = true; + } else { + initLoginInfoView(); + } + } } } else { updateServerStatusIconAndText(result); @@ -928,6 +1129,31 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } } + // region LoginInfoView + private void initLoginInfoView() { + LinearLayout loginFlowLayout = accountSetupWebviewBinding.loginFlowV2.getRoot(); + MaterialButton cancelButton = accountSetupWebviewBinding.loginFlowV2.cancelButton; + loginFlowLayout.setVisibility(View.VISIBLE); + + // add margin bottom to prevent overlapping with system bars + ViewCompat.setOnApplyWindowInsetsListener(loginFlowLayout, (view, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + view.setPadding( + view.getPaddingLeft(), + view.getPaddingTop(), + view.getPaddingRight(), + systemBars.bottom); + return insets; + }); + + cancelButton.setOnClickListener(v -> { + loginFlowExecutorService.shutdown(); + ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); + recreate(); + }); + } + // endregion + /** * Chooses the right icon and text to show to the user for the received operation result. * @@ -1139,10 +1365,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity accountManager.setCurrentOwnCloudAccount(mAccount.name); getUserCapabilitiesAndFinish(); } else { - // init webView again - if (accountSetupWebviewBinding != null) { - accountSetupWebviewBinding.loginWebview.setVisibility(View.GONE); - } accountSetupBinding = AccountSetupBinding.inflate(getLayoutInflater()); setContentView(accountSetupBinding.getRoot()); initOverallUi(); @@ -1169,12 +1391,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } else { // authorization fail due to client side - probably wrong credentials if (accountSetupWebviewBinding != null) { - initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false); - DisplayUtils.showSnackMessage(this, - accountSetupWebviewBinding.loginWebview, R.string.auth_access_failed, - result.getLogMessage()); + anonymouslyPostLoginRequest(mServerInfo.mBaseUrl + WEB_LOGIN); } else { - DisplayUtils.showSnackMessage(this, R.string.auth_access_failed, result.getLogMessage()); + DisplayUtils.showSnackMessage(this, R.string.auth_access_failed, result.getLogMessage(this)); // init webView again updateAuthStatusIconAndText(result); @@ -1190,14 +1409,19 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } private void endSuccess() { - if (onlyAdd) { - finish(); - } else { - Intent i = new Intent(this, FileDisplayActivity.class); - i.setAction(FileDisplayActivity.RESTART); - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(i); + if (!onlyAdd) { + if (MDMConfig.INSTANCE.enforceProtection(this) && Objects.equals(preferences.getLockPreference(), SettingsActivity.LOCK_NONE)) { + Intent i = new Intent(this, SettingsActivity.class); + startActivity(i); + } else { + Intent i = new Intent(this, FileDisplayActivity.class); + i.setAction(FileDisplayActivity.RESTART); + i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + } } + + finish(); } private void getUserCapabilitiesAndFinish() { @@ -1270,7 +1494,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity // can be anything: email, name, name with whitespaces String loginName = webViewUser; - String accountName = com.owncloud.android.lib.common.accounts.AccountUtils.buildAccountName(uri, loginName); + String accountName = AccountUtils.buildAccountName(uri, loginName); Account newAccount = new Account(accountName, accountType); if (accountManager.exists(newAccount)) { // fail - not a new account, but an existing one; disallow @@ -1340,14 +1564,42 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private void startQRScanner() { Intent intent = new Intent(this, QrCodeActivity.class); - startActivityForResult(intent, REQUEST_CODE_QR_SCAN); + qrScanResultLauncher.launch(intent); } + private final ActivityResultLauncher qrScanResultLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + Intent data = result.getData(); + + if (data == null) { + return; + } + + String resultData = data.getStringExtra("com.blikoon.qrcodescanner.got_qr_scan_relult"); + + if (resultData == null || !resultData.startsWith(getString(R.string.login_data_own_scheme))) { + mServerStatusIcon = R.drawable.ic_alert; + mServerStatusText = "QR Code could not be read!"; + showServerStatus(); + return; + } + + if (!MDMConfig.INSTANCE.multiAccountSupport(this) && + accountManager.getAccounts().length == 1) { + Toast.makeText(this, R.string.no_mutliple_accounts_allowed, Toast.LENGTH_LONG).show(); + } else { + parseAndLoginFromWebView(resultData); + } + } + }); + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (requestCode == PermissionUtil.PERMISSIONS_CAMERA) {// If request is cancelled, result arrays are empty. + if (requestCode == PERMISSIONS_CAMERA) {// If request is cancelled, result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted startQRScanner(); @@ -1362,7 +1614,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity * server. */ private void showServerStatus() { - if (accountSetupBinding == null) return; + if (accountSetupBinding == null) { + return; + } if (mServerStatusIcon == NO_ICON && EMPTY_STRING.equals(mServerStatusText)) { accountSetupBinding.serverStatusText.setVisibility(View.INVISIBLE); @@ -1426,12 +1680,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } } - private void dismissWaitingDialog() { Fragment frag = getSupportFragmentManager().findFragmentByTag(WAIT_DIALOG_TAG); - if (frag instanceof DialogFragment) { - DialogFragment dialog = (DialogFragment) frag; - + if (frag instanceof DialogFragment dialog) { try { dialog.dismiss(); } catch (IllegalStateException e) { @@ -1459,9 +1710,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity String prefix = getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/"; LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, data.toString()); - mServerInfo.mBaseUrl = AuthenticatorUrlUtils.INSTANCE.normalizeUrlSuffix(loginUrlInfo.serverAddress); - webViewUser = loginUrlInfo.username; - webViewPassword = loginUrlInfo.password; + mServerInfo.mBaseUrl = AuthenticatorUrlUtils.INSTANCE.normalizeUrlSuffix(loginUrlInfo.getServer()); + webViewUser = loginUrlInfo.getLoginName(); + webViewPassword = loginUrlInfo.getAppPassword(); doOnResumeAndBound(); checkOcServer(); } catch (Exception e) { @@ -1486,31 +1737,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } } - @Override - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CODE_QR_SCAN) { - if (data == null) { - return; - } - - String result = data.getStringExtra("com.blikoon.qrcodescanner.got_qr_scan_relult"); - - if (result == null || !result.startsWith(getString(R.string.login_data_own_scheme))) { - mServerStatusIcon = R.drawable.ic_alert; - mServerStatusText = "QR Code could not be read!"; - showServerStatus(); - return; - } - - if (!getResources().getBoolean(R.bool.multiaccount_support) && - accountManager.getAccounts().length == 1) { - Toast.makeText(this, R.string.no_mutliple_accounts_allowed, Toast.LENGTH_LONG).show(); - } else { - parseAndLoginFromWebView(result); - } - } - } /** * Called from SslValidatorDialog when a new server certificate was correctly saved. diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt index 29d6ce0..0a66a90 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt @@ -1,11 +1,11 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2013-2015 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ @file:Suppress("DEPRECATION") @@ -13,8 +13,8 @@ package com.owncloud.android.authentication import android.app.Activity import android.content.Context -import android.net.Uri import android.os.AsyncTask +import androidx.core.net.toUri import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.OwnCloudClientFactory import com.owncloud.android.lib.common.OwnCloudCredentials @@ -46,7 +46,7 @@ class AuthenticatorAsyncTask(activity: Activity) : AsyncTask + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2017 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.authentication @@ -14,7 +14,7 @@ import java.net.URI */ object AuthenticatorUrlUtils { - const val WEBDAV_PATH_4_0_AND_LATER = "/remote.php/webdav" + private const val REMOTE_PHP_PATH = "/remote.php/dav" fun normalizeUrlSuffix(url: String): String { var normalizedUrl = url @@ -29,19 +29,17 @@ object AuthenticatorUrlUtils { while (trimmedUrl.endsWith("/")) { trimmedUrl = trimmedUrl.substring(0, url.length - 1) } - val pos = trimmedUrl.lastIndexOf(WEBDAV_PATH_4_0_AND_LATER) + val pos = trimmedUrl.lastIndexOf(REMOTE_PHP_PATH) if (pos >= 0) { trimmedUrl = trimmedUrl.substring(0, pos) } return trimmedUrl } - private fun trimUrlWebdav(url: String): String { - return if (url.lowercase().endsWith(WEBDAV_PATH_4_0_AND_LATER)) { - url.substring(0, url.length - WEBDAV_PATH_4_0_AND_LATER.length) - } else { - url - } + private fun trimUrlWebdav(url: String): String = if (url.lowercase().endsWith(REMOTE_PHP_PATH)) { + url.substring(0, url.length - REMOTE_PHP_PATH.length) + } else { + url } fun stripIndexPhpOrAppsFiles(url: String): String { @@ -54,13 +52,11 @@ object AuthenticatorUrlUtils { return strippedUrl } - fun normalizeScheme(url: String): String { - return if (url.matches("[a-zA-Z][a-zA-Z0-9+.-]+://.+".toRegex())) { - val uri = URI.create(url) - val lcScheme = uri.scheme.lowercase() - String.format("%s:%s", lcScheme, uri.rawSchemeSpecificPart) - } else { - url - } + fun normalizeScheme(url: String): String = if (url.matches("[a-zA-Z][a-zA-Z0-9+.-]+://.+".toRegex())) { + val uri = URI.create(url) + val lcScheme = uri.scheme.lowercase() + String.format("%s:%s", lcScheme, uri.rawSchemeSpecificPart) + } else { + url } } diff --git a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt index 24d9faf..610af50 100644 --- a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt +++ b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2018-2022 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.authentication @@ -11,16 +11,16 @@ import android.os.Bundle import android.widget.TextView import android.widget.Toast import com.nextcloud.client.di.Injectable +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.R -class DeepLinkLoginActivity : AuthenticatorActivity(), Injectable { +class DeepLinkLoginActivity : + AuthenticatorActivity(), + Injectable { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - if (!resources.getBoolean(R.bool.multiaccount_support) && - accountManager.accounts.size == 1 - ) { + if (!MDMConfig.multiAccountSupport(this) && accountManager.accounts.size == 1) { Toast.makeText(this, R.string.no_mutliple_accounts_allowed, Toast.LENGTH_LONG).show() return } @@ -33,8 +33,9 @@ class DeepLinkLoginActivity : AuthenticatorActivity(), Injectable { val loginUrlInfo = parseLoginDataUrl(prefix, it.toString()) val loginText = findViewById(R.id.loginInfo) loginText.text = String.format( - getString(R.string.direct_login_text), loginUrlInfo.username, - loginUrlInfo.serverAddress + getString(R.string.direct_login_text), + loginUrlInfo.loginName, + loginUrlInfo.server ) } catch (e: IllegalArgumentException) { Toast.makeText(this, R.string.direct_login_failed, Toast.LENGTH_LONG).show() diff --git a/app/src/main/java/com/owncloud/android/authentication/EnforcedServer.kt b/app/src/main/java/com/owncloud/android/authentication/EnforcedServer.kt new file mode 100644 index 0000000..b9483d2 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/authentication/EnforcedServer.kt @@ -0,0 +1,10 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Tobias Kaminsky + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.authentication + +data class EnforcedServer(val name: String, val url: String) diff --git a/app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.java b/app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.java deleted file mode 100644 index 026b556..0000000 --- a/app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2016 Andy Scherzinger - * SPDX-FileCopyrightText: 2016 Nextcloud - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.authentication; - -/** - * Data object holding the login url fields. - */ -public class LoginUrlInfo { - String serverAddress; - String username; - String password; -} diff --git a/app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.kt b/app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.kt new file mode 100644 index 0000000..a752d40 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/authentication/LoginUrlInfo.kt @@ -0,0 +1,20 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2016 Andy Scherzinger + * SPDX-FileCopyrightText: 2016 Nextcloud + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.authentication + +import com.nextcloud.model.HTTPStatusCodes + +data class LoginUrlInfo(var server: String, var loginName: String, var appPassword: String) { + fun isValid(status: Int): Boolean = ( + status == HTTPStatusCodes.SUCCESS.code && + server.isNotEmpty() && + loginName.isNotEmpty() && + appPassword.isNotEmpty() + ) +} diff --git a/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt b/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt index 2564806..4694629 100644 --- a/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt +++ b/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2022-2023 Álvaro Brey - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.authentication @@ -37,19 +37,28 @@ class PassCodeManager(private val preferences: AppPreferences, private val clock * the pass code being requested on screen rotations. */ private const val PASS_CODE_TIMEOUT = 5000 + + fun setSecureFlag(activity: Activity, isSet: Boolean) { + activity.window?.let { window -> + if (isSet) { + println("flag added") + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } else { + println("flag cleared") + window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + } + } } var canAskPin = true private var askPinWhenDeviceLocked = false - private fun isExemptActivity(activity: Activity): Boolean { - return exemptOfPasscodeActivities.contains(activity.javaClass) - } + private fun isExemptActivity(activity: Activity): Boolean = exemptOfPasscodeActivities.contains(activity.javaClass) fun onActivityResumed(activity: Activity): Boolean { var askedForPin = false val timestamp = preferences.lockTimestamp - setSecureFlag(activity) if (!isExemptActivity(activity)) { val passcodeRequested = passCodeShouldBeRequested(timestamp) @@ -76,16 +85,6 @@ class PassCodeManager(private val preferences: AppPreferences, private val clock return askedForPin } - private fun setSecureFlag(activity: Activity) { - activity.window?.let { window -> - if (isPassCodeEnabled() || deviceCredentialsAreEnabled(activity)) { - window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) - } else { - window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) - } - } - } - private fun requestPasscode(activity: Activity) { val i = Intent(MainApp.getAppContext(), PassCodeActivity::class.java).apply { action = PassCodeActivity.ACTION_CHECK @@ -116,28 +115,21 @@ class PassCodeManager(private val preferences: AppPreferences, private val clock /** * `true` if the time elapsed since last unlock is longer than [PASS_CODE_TIMEOUT] and no activities are visible */ - private fun shouldBeLocked(timestamp: Long): Boolean { - return (abs(clock.millisSinceBoot - timestamp) > PASS_CODE_TIMEOUT && canAskPin) || askPinWhenDeviceLocked - } + private fun shouldBeLocked(timestamp: Long): Boolean = + (abs(clock.millisSinceBoot - timestamp) > PASS_CODE_TIMEOUT && canAskPin) || askPinWhenDeviceLocked @VisibleForTesting - fun passCodeShouldBeRequested(timestamp: Long): Boolean { - return shouldBeLocked(timestamp) && isPassCodeEnabled() - } + fun passCodeShouldBeRequested(timestamp: Long): Boolean = shouldBeLocked(timestamp) && isPassCodeEnabled() private fun isPassCodeEnabled(): Boolean = SettingsActivity.LOCK_PASSCODE == preferences.lockPreference - private fun deviceCredentialsShouldBeRequested(timestamp: Long, activity: Activity): Boolean { - return shouldBeLocked(timestamp) && deviceCredentialsAreEnabled(activity) - } + private fun deviceCredentialsShouldBeRequested(timestamp: Long, activity: Activity): Boolean = + shouldBeLocked(timestamp) && deviceCredentialsAreEnabled(activity) - private fun deviceCredentialsAreEnabled(activity: Activity): Boolean { - return SettingsActivity.LOCK_DEVICE_CREDENTIALS == preferences.lockPreference || - (preferences.isFingerprintUnlockEnabled && DeviceCredentialUtils.areCredentialsAvailable(activity)) - } + private fun deviceCredentialsAreEnabled(activity: Activity): Boolean = + (preferences.lockPreference == SettingsActivity.LOCK_DEVICE_CREDENTIALS) && + DeviceCredentialUtils.areCredentialsAvailable(activity) - private fun getActivityRootView(activity: Activity): View? { - return activity.window?.findViewById(android.R.id.content) - ?: activity.window?.decorView?.findViewById(android.R.id.content) - } + private fun getActivityRootView(activity: Activity): View? = activity.window?.findViewById(android.R.id.content) + ?: activity.window?.decorView?.findViewById(android.R.id.content) } diff --git a/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.kt b/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.kt index 466fbb7..c7b40aa 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProviderImpl.java b/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProviderImpl.java index 950eedf..1acd7f3 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProviderImpl.java +++ b/app/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProviderImpl.java @@ -5,7 +5,7 @@ * Copyright (C) 2017 Mario Danic * Copyright (C) 2017 Nextcloud. * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/main/java/com/owncloud/android/datamodel/ContentResolverHelper.kt b/app/src/main/java/com/owncloud/android/datamodel/ContentResolverHelper.kt index 9be600d..4f2f604 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ContentResolverHelper.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/ContentResolverHelper.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Álvaro Brey * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel @@ -83,22 +83,20 @@ object ContentResolverHelper { sortColumn: String?, sortDirection: String?, limit: Int? - ): Bundle { - return Bundle().apply { - if (selection != null) { - putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection) - } - if (sortColumn != null) { - putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, arrayOf(sortColumn)) - val direction = when (sortDirection) { - SORT_DIRECTION_ASCENDING -> ContentResolver.QUERY_SORT_DIRECTION_ASCENDING - else -> ContentResolver.QUERY_SORT_DIRECTION_DESCENDING - } - putInt(ContentResolver.QUERY_ARG_SORT_DIRECTION, direction) - } - if (limit != null) { - putInt(ContentResolver.QUERY_ARG_LIMIT, limit) + ): Bundle = Bundle().apply { + if (selection != null) { + putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection) + } + if (sortColumn != null) { + putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, arrayOf(sortColumn)) + val direction = when (sortDirection) { + SORT_DIRECTION_ASCENDING -> ContentResolver.QUERY_SORT_DIRECTION_ASCENDING + else -> ContentResolver.QUERY_SORT_DIRECTION_DESCENDING } + putInt(ContentResolver.QUERY_ARG_SORT_DIRECTION, direction) + } + if (limit != null) { + putInt(ContentResolver.QUERY_ARG_LIMIT, limit) } } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/DecryptedFolderMetadataOld.java b/app/src/main/java/com/owncloud/android/datamodel/DecryptedFolderMetadataOld.java index f1977e1..98712fb 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/DecryptedFolderMetadataOld.java +++ b/app/src/main/java/com/owncloud/android/datamodel/DecryptedFolderMetadataOld.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/main/java/com/owncloud/android/datamodel/DecryptedPushMessage.kt b/app/src/main/java/com/owncloud/android/datamodel/DecryptedPushMessage.kt index c4fdb52..301a149 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/DecryptedPushMessage.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/DecryptedPushMessage.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2022 Unpublished - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/main/java/com/owncloud/android/datamodel/EncryptedFiledrop.kt b/app/src/main/java/com/owncloud/android/datamodel/EncryptedFiledrop.kt index bfab7f8..9d035aa 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/EncryptedFiledrop.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/EncryptedFiledrop.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java b/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java index 79f13db..a50be70 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java @@ -4,7 +4,7 @@ * Copyright (C) 2017 Tobias Kaminsky * Copyright (C) 2017 Nextcloud. * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; @@ -43,31 +43,26 @@ public class ExternalLinksProvider { * Stores an external link in database. * * @param externalLink object to store - * @return external link id, -1 if the insert process fails. */ - public long storeExternalLink(ExternalLink externalLink) { + public void storeExternalLink(ExternalLink externalLink) { Log_OC.v(TAG, "Adding " + externalLink.getName()); ContentValues cv = createContentValuesFromExternalLink(externalLink); Uri result = mContentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, cv); - if (result != null) { - return Long.parseLong(result.getPathSegments().get(1)); - } else { + if (result == null) { Log_OC.e(TAG, "Failed to insert item " + externalLink.getName() + " into external link db."); - return -1; } } /** * Delete all external links from the db - * @return numbers of rows deleted */ - public int deleteAllExternalLinks() { - return mContentResolver.delete(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, - null, - null); + public void deleteAllExternalLinks() { + mContentResolver.delete(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, + null, + null); } /** @@ -138,22 +133,13 @@ public class ExternalLinksProvider { ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_ICON_URL)); String language = cursor.getString(cursor.getColumnIndexOrThrow( ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE)); - ExternalLinkType type; - switch (cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE))) { - case "link": - type = ExternalLinkType.LINK; - break; - case "settings": - type = ExternalLinkType.SETTINGS; - break; - case "quota": - type = ExternalLinkType.QUOTA; - break; - default: - type = ExternalLinkType.UNKNOWN; - break; - } + ExternalLinkType type = switch (cursor.getString(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE))) { + case "link" -> ExternalLinkType.LINK; + case "settings" -> ExternalLinkType.SETTINGS; + case "quota" -> ExternalLinkType.QUOTA; + default -> ExternalLinkType.UNKNOWN; + }; String name = cursor.getString(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_NAME)); String url = cursor.getString(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_URL)); boolean redirect = cursor.getInt( diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index f930873..517f402 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1,18 +1,19 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk - * SPDX-FileCopyrightText: 2022 TSI-mc + * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2022-2025 TSI-mc * SPDX-FileCopyrightText: 2021 Chris Narkiewicz * SPDX-FileCopyrightText: 2018-2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2012 David A. Velasco * SPDX-FileCopyrightText: 2011 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datamodel; +import android.annotation.SuppressLint; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; @@ -31,10 +32,22 @@ import android.text.TextUtils; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; +import com.nextcloud.android.lib.resources.files.FileDownloadLimit; import com.nextcloud.client.account.User; import com.nextcloud.client.database.NextcloudDatabase; import com.nextcloud.client.database.dao.FileDao; +import com.nextcloud.client.database.dao.OfflineOperationDao; +import com.nextcloud.client.database.dao.RecommendedFileDao; import com.nextcloud.client.database.entity.FileEntity; +import com.nextcloud.client.database.entity.OfflineOperationEntity; +import com.nextcloud.client.jobs.offlineOperations.repository.OfflineOperationsRepository; +import com.nextcloud.client.jobs.offlineOperations.repository.OfflineOperationsRepositoryType; +import com.nextcloud.model.OCFileFilterType; +import com.nextcloud.model.OfflineOperationRawType; +import com.nextcloud.model.OfflineOperationType; +import com.nextcloud.model.ShareeEntry; +import com.nextcloud.utils.date.DateFormatPattern; +import com.nextcloud.utils.extensions.DateExtensionsKt; import com.owncloud.android.MainApp; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.lib.common.network.WebdavEntry; @@ -45,12 +58,14 @@ import com.owncloud.android.lib.resources.files.model.FileLockType; import com.owncloud.android.lib.resources.files.model.GeoLocation; import com.owncloud.android.lib.resources.files.model.ImageDimension; import com.owncloud.android.lib.resources.files.model.RemoteFile; +import com.owncloud.android.lib.resources.files.model.ServerFileInterface; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.shares.ShareeUser; import com.owncloud.android.lib.resources.status.CapabilityBooleanType; import com.owncloud.android.lib.resources.status.E2EVersion; import com.owncloud.android.lib.resources.status.OCCapability; +import com.owncloud.android.lib.resources.tags.Tag; import com.owncloud.android.operations.RemoteOperationFailedException; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.MimeType; @@ -64,6 +79,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -76,6 +92,7 @@ import androidx.annotation.VisibleForTesting; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import kotlin.Pair; +@SuppressFBWarnings("CE") public class FileDataStorageManager { private static final String TAG = FileDataStorageManager.class.getSimpleName(); @@ -92,19 +109,25 @@ public class FileDataStorageManager { private final ContentProviderClient contentProviderClient; private final User user; - private final FileDao fileDao = NextcloudDatabase.getInstance(MainApp.getAppContext()).fileDao(); + public final RecommendedFileDao recommendedFileDao = NextcloudDatabase.getInstance(MainApp.getAppContext()).recommendedFileDao(); + public final OfflineOperationDao offlineOperationDao = NextcloudDatabase.getInstance(MainApp.getAppContext()).offlineOperationDao(); + public final FileDao fileDao = NextcloudDatabase.getInstance(MainApp.getAppContext()).fileDao(); private final Gson gson = new Gson(); + public final OfflineOperationsRepositoryType offlineOperationsRepository; + private final static int DEFAULT_CURSOR_INT_VALUE = -1; public FileDataStorageManager(User user, ContentResolver contentResolver) { this.contentProviderClient = null; this.contentResolver = contentResolver; this.user = user; + offlineOperationsRepository = new OfflineOperationsRepository(this); } public FileDataStorageManager(User user, ContentProviderClient contentProviderClient) { this.contentProviderClient = contentProviderClient; this.contentResolver = null; this.user = user; + offlineOperationsRepository = new OfflineOperationsRepository(this); } /** @@ -124,6 +147,198 @@ public class FileDataStorageManager { return getFileByPath(ProviderTableMeta.FILE_PATH_DECRYPTED, path); } + public void addCreateFileOfflineOperation(String[] localPaths, String[] remotePaths) { + if (localPaths.length != remotePaths.length) { + Log_OC.d(TAG, "Local path and remote path size do not match"); + return; + } + + for (int i = 0; i < localPaths.length; i++) { + String localPath = localPaths[i]; + String remotePath = remotePaths[i]; + String mimeType = MimeTypeUtil.getMimeTypeFromPath(remotePath); + + OfflineOperationEntity entity = new OfflineOperationEntity(); + entity.setPath(remotePath); + entity.setType(new OfflineOperationType.CreateFile(OfflineOperationRawType.CreateFile.name(), localPath, remotePath, mimeType)); + + long createdAt = System.currentTimeMillis(); + long modificationTimestamp = System.currentTimeMillis(); + + entity.setCreatedAt(createdAt); + entity.setModifiedAt(modificationTimestamp / 1000); + entity.setFilename(new File(remotePath).getName()); + + String parentPath = new File(remotePath).getParent() + OCFile.PATH_SEPARATOR; + OCFile parentFile = getFileByDecryptedRemotePath(parentPath); + + if (parentFile != null) { + entity.setParentOCFileId(parentFile.getFileId()); + } + + offlineOperationDao.insert(entity); + createPendingFile(remotePath, mimeType, createdAt, modificationTimestamp); + } + } + + public OfflineOperationEntity getOfflineEntityFromOCFile(OCFile file) { + return offlineOperationDao.getByPath(file.getDecryptedRemotePath()); + } + + public OfflineOperationEntity addCreateFolderOfflineOperation(String path, String filename, Long parentOCFileId) { + OfflineOperationEntity entity = new OfflineOperationEntity(); + + entity.setFilename(filename); + entity.setParentOCFileId(parentOCFileId); + + OfflineOperationType.CreateFolder operationType = new OfflineOperationType.CreateFolder(OfflineOperationRawType.CreateFolder.name(), path); + entity.setType(operationType); + entity.setPath(path); + + long createdAt = System.currentTimeMillis(); + long modificationTimestamp = System.currentTimeMillis(); + + entity.setCreatedAt(createdAt); + entity.setModifiedAt(modificationTimestamp / 1000); + + offlineOperationDao.insert(entity); + createPendingDirectory(path, createdAt, modificationTimestamp); + + return entity; + } + + public void createPendingFile(String path, String mimeType, long createdAt, long modificationTimestamp) { + OCFile file = new OCFile(path); + file.setMimeType(mimeType); + file.setCreationTimestamp(createdAt); + file.setModificationTimestamp(modificationTimestamp); + saveFileWithParent(file, MainApp.getAppContext()); + } + + public void createPendingDirectory(String path, long createdAt, long modificationTimestamp) { + OCFile directory = new OCFile(path); + directory.setMimeType(MimeType.DIRECTORY); + directory.setCreationTimestamp(createdAt); + directory.setModificationTimestamp(modificationTimestamp); + saveFileWithParent(directory, MainApp.getAppContext()); + } + + public void deleteOfflineOperation(OCFile file) { + offlineOperationsRepository.deleteOperation(file); + } + + public void addRenameFileOfflineOperation(OCFile file, String newName) { + OfflineOperationEntity entity = new OfflineOperationEntity(); + + entity.setFilename(newName); + entity.setParentOCFileId(file.getParentId()); + + OfflineOperationType operationType = new OfflineOperationType.RenameFile(OfflineOperationRawType.RenameFile.name(), file.getFileId(), newName); + entity.setType(operationType); + entity.setPath(file.getDecryptedRemotePath()); + + long createdAt = System.currentTimeMillis(); + long modificationTimestamp = System.currentTimeMillis(); + + entity.setCreatedAt(createdAt); + entity.setModifiedAt(modificationTimestamp / 1000); + + offlineOperationDao.insert(entity); + } + + public String getFileNameBasedOnEncryptionStatus(OCFile file) { + FileEntity entity = fileDao.getFileById(file.getFileId()); + if (entity == null) { + return file.getFileName(); + } + + if (file.isEncrypted()) { + return entity.getEncryptedName(); + } else { + return entity.getName(); + } + } + + public String getFilenameConsideringOfflineOperation(OCFile file) { + String filename = file.getDecryptedFileName(); + OfflineOperationEntity renameEntity = offlineOperationDao.getByPath(file.getDecryptedRemotePath()); + if (renameEntity != null && renameEntity.getType() instanceof OfflineOperationType.RenameFile renameFile) { + filename = renameFile.getNewName(); + } + + return filename; + } + + public void addRemoveFileOfflineOperation(@NonNull OCFile file) { + OfflineOperationEntity entity = new OfflineOperationEntity(); + + String path = file.getDecryptedRemotePath(); + entity.setFilename(file.getFileName()); + entity.setParentOCFileId(file.getParentId()); + + OfflineOperationType.RemoveFile operationType = new OfflineOperationType.RemoveFile(OfflineOperationRawType.RemoveFile.name(), path); + entity.setType(operationType); + entity.setPath(path); + + long createdAt = System.currentTimeMillis(); + long modificationTimestamp = System.currentTimeMillis(); + + entity.setCreatedAt(createdAt); + entity.setModifiedAt(modificationTimestamp / 1000); + + offlineOperationDao.insert(entity); + } + + public void renameOfflineOperation(OCFile file, String newFolderName) { + var entity = offlineOperationDao.getByPath(file.getDecryptedRemotePath()); + if (entity == null) { + return; + } + + OCFile parentFolder = getFileById(file.getParentId()); + if (parentFolder == null) { + return; + } + + String newPath = parentFolder.getDecryptedRemotePath() + newFolderName + OCFile.PATH_SEPARATOR; + + if (entity.getType() instanceof OfflineOperationType.CreateFolder createFolderType) { + createFolderType.setPath(newPath); + } else if (entity.getType() instanceof OfflineOperationType.CreateFile createFileType) { + createFileType.setRemotePath(newPath); + createFileType.setMimeType(file.getMimeType()); + } + entity.setType(entity.getType()); + + entity.setPath(newPath); + entity.setFilename(newFolderName); + offlineOperationDao.update(entity); + + moveLocalFile(file, newPath, parentFolder.getDecryptedRemotePath()); + } + + @SuppressLint("SimpleDateFormat") + public void keepOfflineOperationAndServerFile(OfflineOperationEntity entity, OCFile file) { + if (file == null) return; + + String oldFileName = entity.getFilename(); + if (oldFileName == null) return; + + Long parentOCFileId = entity.getParentOCFileId(); + if (parentOCFileId == null) return; + + OCFile parentFolder = getFileById(parentOCFileId); + if (parentFolder == null) return; + + DateFormatPattern formatPattern = DateFormatPattern.FullDateWithHours; + String currentDateTime = DateExtensionsKt.currentDateRepresentation(new Date(), formatPattern); + + String newFolderName = oldFileName + " - " + currentDateTime; + String newPath = parentFolder.getDecryptedRemotePath() + newFolderName + OCFile.PATH_SEPARATOR; + moveLocalFile(file, newPath, parentFolder.getDecryptedRemotePath()); + offlineOperationsRepository.updateNextOperations(entity); + } + private @Nullable OCFile getFileByPath(String type, String path) { final boolean shouldUseEncryptedPath = ProviderTableMeta.FILE_PATH.equals(type); @@ -151,6 +366,15 @@ public class FileDataStorageManager { return null; } + public @Nullable + OCFile getFileByLocalId(long localId) { + FileEntity fileEntity = fileDao.getFileByLocalId(localId); + if (fileEntity != null) { + return createFileInstance(fileEntity); + } + return null; + } + public @Nullable OCFile getFileByLocalPath(String path) { FileEntity fileEntity = fileDao.getFileByLocalPath(path, user.getAccountName()); @@ -169,12 +393,19 @@ public class FileDataStorageManager { return null; } - public boolean fileExists(long id) { return fileDao.getFileById(id) != null; } + public boolean fileExists(long id) { + return fileDao.getFileById(id) != null; + } public boolean fileExists(String path) { return fileDao.getFileByEncryptedRemotePath(path, user.getAccountName()) != null; } + public OCFile getTopParent(OCFile file) { + long topParentId = getTopParentId(file); + return getFileById(topParentId); + } + public long getTopParentId(OCFile file) { if (file.getParentId() == 1) { return file.getFileId(); @@ -230,6 +461,23 @@ public class FileDataStorageManager { } } + public OCFile findDuplicatedFile(OCFile parentFolder, ServerFileInterface newFile) { + List folderContent = getFolderContent(parentFolder, false); + if (folderContent == null || folderContent.isEmpty()) { + return null; + } + + OCFile duplicatedFile = null; + for (OCFile file : folderContent) { + if (file.getFileName().equals(newFile.getFileName())) { + duplicatedFile = file; + break; + } + } + + return duplicatedFile; + } + public List getFolderImages(OCFile folder, boolean onlyOnDevice) { List imageList = new ArrayList<>(); @@ -325,7 +573,7 @@ public class FileDataStorageManager { } else { Exception exception = result.getException(); String message = "Error during saving file with parents: " + ocFile.getRemotePath() + " / " - + result.getLogMessage(); + + result.getLogMessage(context); if (exception != null) { throw new RemoteOperationFailedException(message, exception); @@ -345,19 +593,19 @@ public class FileDataStorageManager { } public static void clearTempEncryptedFolder(String accountName) { - File tempEncryptedFolder = new File(FileStorageUtils.getTemporalEncryptedFolderPath(accountName)); + File tempEncryptedFolder = new File(FileStorageUtils.getTemporalEncryptedFolderPath(accountName)); if (!tempEncryptedFolder.exists()) { - Log_OC.d(TAG,"tempEncryptedFolder does not exist"); + Log_OC.d(TAG, "tempEncryptedFolder does not exist"); return; } try { FileUtils.cleanDirectory(tempEncryptedFolder); - Log_OC.d(TAG,"tempEncryptedFolder cleared"); + Log_OC.d(TAG, "tempEncryptedFolder cleared"); } catch (IOException exception) { - Log_OC.d(TAG,"Error caught at clearTempEncryptedFolder: " + exception); + Log_OC.d(TAG, "Error caught at clearTempEncryptedFolder: " + exception); } } @@ -390,7 +638,7 @@ public class FileDataStorageManager { /** * Inserts or updates the list of files contained in a given folder. - * + *

    * CALLER IS RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD. HERE ONLY DATA CONSISTENCY * SHOULD BE GRANTED * @@ -438,7 +686,7 @@ public class FileDataStorageManager { whereArgs[1] = ocFile.getRemotePath(); if (ocFile.isFolder()) { operations.add(ContentProviderOperation.newDelete( - ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, ocFile.getFileId())) + ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, ocFile.getFileId())) .withSelection(where, whereArgs).build()); File localFolder = new File(FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), ocFile)); @@ -447,7 +695,7 @@ public class FileDataStorageManager { } } else { operations.add(ContentProviderOperation.newDelete( - ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, ocFile.getFileId())) + ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, ocFile.getFileId())) .withSelection(where, whereArgs).build()); if (ocFile.isDown()) { @@ -507,14 +755,17 @@ public class FileDataStorageManager { /** * Returns a {@link ContentValues} filled with values that are common to both files and folders + * * @see #createContentValuesForFile(OCFile) * @see #createContentValuesForFolder(OCFile) */ + @SuppressFBWarnings("CE") private ContentValues createContentValuesBase(OCFile fileOrFolder) { final ContentValues cv = new ContentValues(); cv.put(ProviderTableMeta.FILE_MODIFIED, fileOrFolder.getModificationTimestamp()); cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, fileOrFolder.getModificationTimestampAtLastSyncForData()); cv.put(ProviderTableMeta.FILE_PARENT, fileOrFolder.getParentId()); + cv.put(ProviderTableMeta.FILE_UPLOADED, fileOrFolder.getUploadTimestamp()); cv.put(ProviderTableMeta.FILE_CREATION, fileOrFolder.getCreationTimestamp()); cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, fileOrFolder.getMimeType()); cv.put(ProviderTableMeta.FILE_NAME, fileOrFolder.getFileName()); @@ -540,11 +791,14 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_SHAREES, gson.toJson(fileOrFolder.getSharees())); cv.put(ProviderTableMeta.FILE_TAGS, gson.toJson(fileOrFolder.getTags())); cv.put(ProviderTableMeta.FILE_RICH_WORKSPACE, fileOrFolder.getRichWorkspace()); + cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP, fileOrFolder.getInternalFolderSyncTimestamp()); + cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_RESULT, fileOrFolder.getInternalFolderSyncResult()); return cv; } /** * Returns a {@link ContentValues} filled with values for a folder + * * @see #createContentValuesForFile(OCFile) * @see #createContentValuesBase(OCFile) */ @@ -556,6 +810,7 @@ public class FileDataStorageManager { /** * Returns a {@link ContentValues} filled with values for a file + * * @see #createContentValuesForFolder(OCFile) * @see #createContentValuesBase(OCFile) */ @@ -584,6 +839,8 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_METADATA_GPS, gson.toJson(file.getGeoLocation())); cv.put(ProviderTableMeta.FILE_METADATA_LIVE_PHOTO, file.getLinkedFileIdForLivePhoto()); cv.put(ProviderTableMeta.FILE_E2E_COUNTER, file.getE2eCounter()); + cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP, file.getInternalFolderSyncTimestamp()); + cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_RESULT, file.getInternalFolderSyncResult()); return cv; } @@ -725,7 +982,7 @@ public class FileDataStorageManager { /** * Updates database and file system for a file or folder that was moved to a different location. - * + *

    * TODO explore better (faster) implementations TODO throw exceptions up ! */ public void moveLocalFile(OCFile ocFile, String targetPath, String targetParentPath) { @@ -750,7 +1007,7 @@ public class FileDataStorageManager { int lengthOfOldPath = oldPath.length(); int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath; - for (FileEntity fileEntity: fileEntities) { + for (FileEntity fileEntity : fileEntities) { ContentValues contentValues = new ContentValues(); // keep construction in the loop OCFile childFile = createFileInstance(fileEntity); contentValues.put( @@ -853,8 +1110,8 @@ public class FileDataStorageManager { } /** - * This method does not require {@link FileDataStorageManager} being initialized - * with any specific user. Migration can be performed with {@link com.nextcloud.client.account.AnonymousUser}. + * This method does not require {@link FileDataStorageManager} being initialized with any specific user. Migration + * can be performed with {@link com.nextcloud.client.account.AnonymousUser}. */ public void migrateStoredFiles(String sourcePath, String destinationPath) throws RemoteException, OperationApplicationException { @@ -886,7 +1143,7 @@ public class FileDataStorageManager { ContentValues cv = new ContentValues(); fileId[0] = String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ProviderTableMeta._ID))); String oldFileStoragePath = - cursor.getString(cursor.getColumnIndexOrThrow(ProviderTableMeta.FILE_STORAGE_PATH)); + cursor.getString(cursor.getColumnIndexOrThrow(ProviderTableMeta.FILE_STORAGE_PATH)); if (oldFileStoragePath.startsWith(sourcePath)) { @@ -917,7 +1174,7 @@ public class FileDataStorageManager { List folderContent = new ArrayList<>(); List files = fileDao.getFolderContent(parentId); - for (FileEntity fileEntity: files) { + for (FileEntity fileEntity : files) { OCFile child = createFileInstance(fileEntity); if (!onlyOnDevice || child.existsOnDevice()) { folderContent.add(child); @@ -928,7 +1185,6 @@ public class FileDataStorageManager { return folderContent; } - private OCFile createRootDir() { OCFile ocFile = new OCFile(OCFile.ROOT_PATH); ocFile.setMimeType(MimeType.DIRECTORY); @@ -953,7 +1209,11 @@ public class FileDataStorageManager { return (i == null) ? 0 : i; } - private OCFile createFileInstance(FileEntity fileEntity) { + private long nullToMinusOne(Long i) { + return (i == null) ? -1L : i; + } + + public OCFile createFileInstance(FileEntity fileEntity) { OCFile ocFile = new OCFile(fileEntity.getPath()); ocFile.setDecryptedRemotePath(fileEntity.getPathDecrypted()); ocFile.setFileId(nullToZero(fileEntity.getId())); @@ -974,6 +1234,7 @@ public class FileDataStorageManager { } } ocFile.setFileLength(nullToZero(fileEntity.getContentLength())); + ocFile.setUploadTimestamp(nullToZero(fileEntity.getUploaded())); ocFile.setCreationTimestamp(nullToZero(fileEntity.getCreation())); ocFile.setModificationTimestamp(nullToZero(fileEntity.getModified())); ocFile.setModificationTimestampAtLastSyncForData(nullToZero(fileEntity.getModifiedAtLastSyncForData())); @@ -1017,6 +1278,7 @@ public class FileDataStorageManager { ocFile.setLivePhoto(fileEntity.getMetadataLivePhoto()); ocFile.setHidden(nullToZero(fileEntity.getHidden()) == 1); ocFile.setE2eCounter(fileEntity.getE2eCounter()); + ocFile.setInternalFolderSyncTimestamp(nullToMinusOne(fileEntity.getInternalTwoWaySync())); String sharees = fileEntity.getSharees(); // Surprisingly JSON deserialization causes significant overhead. @@ -1040,7 +1302,7 @@ public class FileDataStorageManager { ocFile.setTags(new ArrayList<>()); } else { try { - String[] tagsArray = gson.fromJson(tags, String[].class); + Tag[] tagsArray = gson.fromJson(tags, Tag[].class); ocFile.setTags(new ArrayList<>(Arrays.asList(tagsArray))); } catch (JsonSyntaxException e) { // ignore saved value due to api change @@ -1179,7 +1441,7 @@ public class FileDataStorageManager { + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?", new String[]{value, user.getAccountName()}, null - ); + ); } else { try { cursor = getContentProviderClient().query( @@ -1188,7 +1450,7 @@ public class FileDataStorageManager { key + AND + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?", new String[]{value, user.getAccountName()}, null - ); + ); } catch (RemoteException e) { Log_OC.w(TAG, "Could not get details, assuming share does not exist: " + e.getMessage()); cursor = null; @@ -1303,6 +1565,11 @@ public class FileDataStorageManager { contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LINK, share.getShareLink()); contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LABEL, share.getLabel()); + FileDownloadLimit downloadLimit = share.getFileDownloadLimit(); + setDownloadLimitToContentValues(contentValues, downloadLimit); + + contentValues.put(ProviderTableMeta.OCSHARES_ATTRIBUTES, share.getAttributes()); + return contentValues; } @@ -1316,7 +1583,8 @@ public class FileDataStorageManager { share.setPermissions(getInt(cursor, ProviderTableMeta.OCSHARES_PERMISSIONS)); share.setSharedDate(getLong(cursor, ProviderTableMeta.OCSHARES_SHARED_DATE)); share.setExpirationDate(getLong(cursor, ProviderTableMeta.OCSHARES_EXPIRATION_DATE)); - share.setToken(getString(cursor, ProviderTableMeta.OCSHARES_TOKEN)); + String token = getString(cursor, ProviderTableMeta.OCSHARES_TOKEN); + share.setToken(token); share.setSharedWithDisplayName(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME)); share.setFolder(getInt(cursor, ProviderTableMeta.OCSHARES_IS_DIRECTORY) == 1); share.setUserId(getString(cursor, ProviderTableMeta.OCSHARES_USER_ID)); @@ -1327,26 +1595,60 @@ public class FileDataStorageManager { share.setShareLink(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LINK)); share.setLabel(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LABEL)); + FileDownloadLimit fileDownloadLimit = getDownloadLimitFromCursor(cursor, token); + if (fileDownloadLimit != null) { + share.setFileDownloadLimit(fileDownloadLimit); + } + + share.setAttributes(getString(cursor, ProviderTableMeta.OCSHARES_ATTRIBUTES)); + return share; } - private void resetShareFlagsInAllFiles() { - ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE); - cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE); - String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; - String[] whereArgs = new String[]{user.getAccountName()}; - - if (getContentResolver() != null) { - getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs); - - } else { - try { - getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs); - } catch (RemoteException e) { - Log_OC.e(TAG, "Exception in resetShareFlagsInAllFiles" + e.getMessage(), e); - } + private void setDownloadLimitToContentValues(ContentValues contentValues, FileDownloadLimit downloadLimit) { + if (downloadLimit != null) { + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT, downloadLimit.getLimit()); + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT, downloadLimit.getCount()); + return; } + + contentValues.putNull(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT); + contentValues.putNull(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT); + } + + @Nullable + private FileDownloadLimit getDownloadLimitFromCursor(Cursor cursor, String token) { + if (token == null || cursor == null) { + return null; + } + + int limit = getIntOrDefault(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT); + int count = getIntOrDefault(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT); + if (limit != DEFAULT_CURSOR_INT_VALUE && count != DEFAULT_CURSOR_INT_VALUE) { + return new FileDownloadLimit(token, limit, count); + } + + return null; + } + + /** + * Retrieves an integer value from the specified column in the cursor. + *

    + * If the column does not exist (i.e., {@code cursor.getColumnIndex(columnName)} returns -1), + * this method returns {@code -1} as a default value. + *

    + * + * @param cursor The Cursor from which to retrieve the value. + * @param columnName The name of the column to retrieve the integer from. + * @return The integer value from the column, or {@code -1} if the column is not found. + */ + private int getIntOrDefault(Cursor cursor, String columnName) { + int index = cursor.getColumnIndex(columnName); + if (index == DEFAULT_CURSOR_INT_VALUE) { + return DEFAULT_CURSOR_INT_VALUE; + } + + return cursor.getInt(index); } private void resetShareFlagsInFolder(OCFile folder) { @@ -1427,21 +1729,19 @@ public class FileDataStorageManager { ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE) .withValues(contentValues) .build() - ); + ); } } // apply operations in batch if (operations.size() > 0) { - @SuppressWarnings("unused") - ContentProviderResult[] results = null; Log_OC.d(TAG, String.format(Locale.ENGLISH, SENDING_TO_FILECONTENTPROVIDER_MSG, operations.size())); try { if (getContentResolver() != null) { - results = getContentResolver().applyBatch(MainApp.getAuthority(), + getContentResolver().applyBatch(MainApp.getAuthority(), operations); } else { - results = getContentProviderClient().applyBatch(operations); + getContentProviderClient().applyBatch(operations); } } catch (OperationApplicationException | RemoteException e) { @@ -1467,6 +1767,67 @@ public class FileDataStorageManager { } } + public void saveSharesFromRemoteFile(List shares) { + if (shares == null || shares.isEmpty()) { + return; + } + + // Prepare reset operations + Set uniquePaths = new HashSet<>(); + for (RemoteFile share : shares) { + uniquePaths.add(share.getRemotePath()); + } + + ArrayList resetOperations = new ArrayList<>(); + for (String path : uniquePaths) { + resetShareFlagInAFile(path); + var removeOps = prepareRemoveSharesInFile(path, new ArrayList<>()); + if (!removeOps.isEmpty()) { + resetOperations.addAll(removeOps); + } + } + if (!resetOperations.isEmpty()) { + applyBatch(resetOperations); + } + + // Prepare insert operations + ArrayList insertOperations = prepareInsertSharesFromRemoteFile(shares); + if (!insertOperations.isEmpty()) { + applyBatch(insertOperations); + } + } + + /** + * Prepares a list of ContentProviderOperation insert operations based on share information + * found in the given iterable of RemoteFile objects. + *

    + * Each RemoteFile may have multiple share entries (sharees), and for each one, + * a corresponding ContentProviderOperation is created for insertion into the shares table. + * + * @param remoteFiles An iterable list of RemoteFile objects containing sharee data. + * @return A list of ContentProviderOperation objects for batch insertion into the content provider. + */ + private ArrayList prepareInsertSharesFromRemoteFile(Iterable remoteFiles) { + final ArrayList contentValueList = new ArrayList<>(); + for (RemoteFile remoteFile : remoteFiles) { + final var contentValues = ShareeEntry.Companion.getContentValues(remoteFile, user.getAccountName()); + if (contentValues == null) { + continue; + } + contentValueList.addAll(contentValues); + } + + ArrayList operations = new ArrayList<>(); + for (ContentValues contentValues : contentValueList) { + operations.add(ContentProviderOperation + .newInsert(ProviderTableMeta.CONTENT_URI_SHARE) + .withValues(contentValues) + .build()); + } + + return operations; + } + public void saveSharesDB(List shares) { ArrayList operations = new ArrayList<>(); @@ -1483,20 +1844,26 @@ public class FileDataStorageManager { // Add operations to insert shares operations = prepareInsertShares(shares, operations); + if (operations.isEmpty()) { + return; + } + // apply operations in batch - if (operations.size() > 0) { - Log_OC.d(TAG, String.format(Locale.ENGLISH, SENDING_TO_FILECONTENTPROVIDER_MSG, operations.size())); - try { - if (getContentResolver() != null) { - getContentResolver().applyBatch(MainApp.getAuthority(), operations); + Log_OC.d(TAG, String.format(Locale.ENGLISH, SENDING_TO_FILECONTENTPROVIDER_MSG, operations.size())); + applyBatch(operations); + } - } else { - getContentProviderClient().applyBatch(operations); - } + private void applyBatch(ArrayList operations) { + try { + if (getContentResolver() != null) { + getContentResolver().applyBatch(MainApp.getAuthority(), operations); - } catch (OperationApplicationException | RemoteException e) { - Log_OC.e(TAG, EXCEPTION_MSG + e.getMessage(), e); + } else { + getContentProviderClient().applyBatch(operations); } + + } catch (OperationApplicationException | RemoteException e) { + Log_OC.e(TAG, EXCEPTION_MSG + e.getMessage(), e); } } @@ -1554,8 +1921,7 @@ public class FileDataStorageManager { * @param operations List of operations * @return */ - private ArrayList prepareInsertShares( - Iterable shares, ArrayList operations) { + private ArrayList prepareInsertShares(Iterable shares, ArrayList operations) { ContentValues contentValues; // prepare operations to insert or update files to save in the given folder @@ -1587,7 +1953,7 @@ public class FileDataStorageManager { ContentProviderOperation.newDelete(ProviderTableMeta.CONTENT_URI_SHARE). withSelection(where, whereArgs). build() - ); + ); } } return preparedOperations; @@ -1605,7 +1971,7 @@ public class FileDataStorageManager { .newDelete(ProviderTableMeta.CONTENT_URI_SHARE) .withSelection(where, whereArgs) .build() - ); + ); return preparedOperations; @@ -1619,17 +1985,19 @@ public class FileDataStorageManager { + ProviderTableMeta.OCSHARES_SHARE_TYPE + " = ? OR " + ProviderTableMeta.OCSHARES_SHARE_TYPE + " = ? OR " + ProviderTableMeta.OCSHARES_SHARE_TYPE + " = ? OR " + + ProviderTableMeta.OCSHARES_SHARE_TYPE + " = ? OR " + ProviderTableMeta.OCSHARES_SHARE_TYPE + " = ? ) "; String[] selectionArgs = new String[]{filePath, accountName, Integer.toString(ShareType.USER.getValue()), Integer.toString(ShareType.GROUP.getValue()), Integer.toString(ShareType.EMAIL.getValue()), Integer.toString(ShareType.FEDERATED.getValue()), + Integer.toString(ShareType.FEDERATED_GROUP.getValue()), Integer.toString(ShareType.ROOM.getValue()), Integer.toString(ShareType.CIRCLE.getValue()) }; - Cursor cursor = null; + Cursor cursor; if (getContentResolver() != null) { cursor = getContentResolver().query(ProviderTableMeta.CONTENT_URI_SHARE, null, @@ -1756,7 +2124,7 @@ public class FileDataStorageManager { cv, ProviderTableMeta._ID + "=?", new String[]{String.valueOf(ocFile.getFileId())} - ); + ); } else { try { updated = getContentProviderClient().update( @@ -1764,7 +2132,7 @@ public class FileDataStorageManager { cv, ProviderTableMeta._ID + "=?", new String[]{String.valueOf(ocFile.getFileId())} - ); + ); } catch (RemoteException e) { Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage(), e); } @@ -1793,20 +2161,20 @@ public class FileDataStorageManager { stringBuilder.append("?)"); if (getContentResolver() != null) { - updated = getContentResolver().update( + getContentResolver().update( ProviderTableMeta.CONTENT_URI_FILE, cv, stringBuilder.toString(), ancestorIds.toArray(new String[]{}) - ); + ); } else { try { - updated = getContentProviderClient().update( + getContentProviderClient().update( ProviderTableMeta.CONTENT_URI_FILE, cv, stringBuilder.toString(), ancestorIds.toArray(new String[]{}) - ); + ); } catch (RemoteException e) { Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage(), e); } @@ -1838,7 +2206,7 @@ public class FileDataStorageManager { whereForDescencentsInConflict, new String[]{user.getAccountName(), parentPath + '%'}, null - ); + ); } else { try { descendentsInConflict = getContentProviderClient().query( @@ -1847,7 +2215,7 @@ public class FileDataStorageManager { whereForDescencentsInConflict, new String[]{user.getAccountName(), parentPath + "%"}, null - ); + ); } catch (RemoteException e) { Log_OC.e(TAG, "Failed querying for descendents in conflict " + e.getMessage(), e); } @@ -1856,22 +2224,22 @@ public class FileDataStorageManager { if (descendentsInConflict == null || descendentsInConflict.getCount() == 0) { Log_OC.d(TAG, "NO MORE conflicts in " + parentPath); if (getContentResolver() != null) { - updated = getContentResolver().update( + getContentResolver().update( ProviderTableMeta.CONTENT_URI_FILE, cv, ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + "=?", new String[]{user.getAccountName(), parentPath} - ); + ); } else { try { - updated = getContentProviderClient().update( + getContentProviderClient().update( ProviderTableMeta.CONTENT_URI_FILE, cv, ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + "=?" , new String[]{user.getAccountName(), parentPath} - ); + ); } catch (RemoteException e) { Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage(), e); } @@ -2030,6 +2398,8 @@ public class FileDataStorageManager { contentValues.put(ProviderTableMeta.CAPABILITIES_USER_STATUS, capability.getUserStatus().getValue()); contentValues.put(ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI, capability.getUserStatusSupportsEmoji().getValue()); + contentValues.put(ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_BUSY, + capability.getUserStatusSupportsBusy().getValue()); contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION, capability.getFilesLockingVersion()); contentValues.put(ProviderTableMeta.CAPABILITIES_ASSISTANT, capability.getAssistant().getValue()); @@ -2037,6 +2407,22 @@ public class FileDataStorageManager { contentValues.put(ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT, capability.getDropAccount().getValue()); contentValues.put(ProviderTableMeta.CAPABILITIES_SECURITY_GUARD, capability.getSecurityGuard().getValue()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAME_CHARACTERS, capability.getForbiddenFilenameCharactersJson()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES, capability.getForbiddenFilenamesJson()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS, capability.getForbiddenFilenameExtensionJson()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES, capability.getForbiddenFilenameBaseNamesJson()); + contentValues.put(ProviderTableMeta.CAPABILITIES_WINDOWS_COMPATIBLE_FILENAMES, capability.isWCFEnabled().getValue()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT, capability.getFilesDownloadLimit().getValue()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT, capability.getFilesDownloadLimitDefault()); + + contentValues.put(ProviderTableMeta.CAPABILITIES_RECOMMENDATION, capability.getRecommendations().getValue()); + + contentValues.put(ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH, capability.getNotesFolderPath()); + + contentValues.put(ProviderTableMeta.CAPABILITIES_DEFAULT_PERMISSIONS, capability.getDefaultPermissions()); + + contentValues.put(ProviderTableMeta.CAPABILITIES_HAS_VALID_SUBSCRIPTION, capability.getHasValidSubscription().getValue()); + return contentValues; } @@ -2199,13 +2585,31 @@ public class FileDataStorageManager { capability.setUserStatus(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_USER_STATUS)); capability.setUserStatusSupportsEmoji( getBoolean(cursor, ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI)); + capability.setUserStatusSupportsBusy( + getBoolean(cursor, ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_BUSY)); capability.setFilesLockingVersion( getString(cursor, ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION)); capability.setAssistant(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_ASSISTANT)); capability.setGroupfolders(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_GROUPFOLDERS)); capability.setDropAccount(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT)); capability.setSecurityGuard(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_SECURITY_GUARD)); + + capability.setForbiddenFilenameCharactersJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAME_CHARACTERS)); + capability.setForbiddenFilenamesJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES)); + capability.setForbiddenFilenameExtensionJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS)); + capability.setForbiddenFilenameBaseNamesJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES)); + capability.setWCFEnabled(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_WINDOWS_COMPATIBLE_FILENAMES)); + capability.setFilesDownloadLimit(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT)); + capability.setFilesDownloadLimitDefault(getInt(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT)); + + capability.setRecommendations(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_RECOMMENDATION)); + + capability.setNotesFolderPath(getString(cursor, ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH)); + + capability.setDefaultPermissions(getInt(cursor, ProviderTableMeta.CAPABILITIES_DEFAULT_PERMISSIONS)); + capability.setHasValidSubscription(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_HAS_VALID_SUBSCRIPTION)); } + return capability; } @@ -2250,7 +2654,7 @@ public class FileDataStorageManager { Log_OC.d(TAG, "getGalleryItems - query complete, list size: " + fileEntities.size()); List files = new ArrayList<>(fileEntities.size()); - for (FileEntity fileEntity: fileEntities) { + for (FileEntity fileEntity : fileEntities) { files.add(createFileInstance(fileEntity)); } @@ -2271,7 +2675,7 @@ public class FileDataStorageManager { ProviderTableMeta.VIRTUAL_TYPE + "=?", new String[]{String.valueOf(type)}, null - ); + ); } catch (RemoteException e) { Log_OC.e(TAG, e.getMessage(), e); return ocFiles; @@ -2283,7 +2687,7 @@ public class FileDataStorageManager { ProviderTableMeta.VIRTUAL_TYPE + "=?", new String[]{String.valueOf(type)}, null - ); + ); } if (c != null) { @@ -2362,7 +2766,7 @@ public class FileDataStorageManager { List files = getAllFilesRecursivelyInsideFolder(folder); List> decryptedFileNamesAndEncryptedRemotePaths = getDecryptedFileNamesAndEncryptedRemotePaths(files); - String decryptedFileName = decryptedRemotePath.substring( decryptedRemotePath.lastIndexOf('/') + 1); + String decryptedFileName = decryptedRemotePath.substring(decryptedRemotePath.lastIndexOf('/') + 1); for (Pair item : decryptedFileNamesAndEncryptedRemotePaths) { if (item.getFirst().equals(decryptedFileName)) { @@ -2399,7 +2803,7 @@ public class FileDataStorageManager { List fileEntities = fileDao.getAllFiles(user.getAccountName()); List folderContent = new ArrayList<>(fileEntities.size()); - for (FileEntity fileEntity: fileEntities) { + for (FileEntity fileEntity : fileEntities) { folderContent.add(createFileInstance(fileEntity)); } @@ -2448,4 +2852,68 @@ public class FileDataStorageManager { return files; } + + public List getInternalTwoWaySyncFolders(User user) { + List fileEntities = fileDao.getInternalTwoWaySyncFolders(user.getAccountName()); + List files = new ArrayList<>(fileEntities.size()); + + for (FileEntity fileEntity : fileEntities) { + OCFile file = createFileInstance(fileEntity); + if (file.isFolder() && !file.isRootDirectory()) { + files.add(file); + } + } + + return files; + } + + public boolean isPartOfInternalTwoWaySync(OCFile file) { + if (file.isInternalFolderSync()) { + return true; + } + + while (file != null && !OCFile.ROOT_PATH.equals(file.getDecryptedRemotePath())) { + if (file.isInternalFolderSync()) { + return true; + } + file = getFileById(file.getParentId()); + } + return false; + } + + public List filter(OCFile file, OCFileFilterType filterType) { + if (!file.isRootDirectory()) { + return getFolderContent(file,false); + } + + final List result = new ArrayList<>(); + final List allFiles = getAllFiles(); + for (OCFile ocFile: allFiles) { + boolean condition = false; + if (filterType == OCFileFilterType.Shared) { + condition = ocFile.isShared(); + } else if (filterType == OCFileFilterType.Favorite) { + condition = ocFile.isFavorite(); + } + + if (condition) { + result.add(ocFile); + } + } + + return result; + } + + @Nullable + public FileEntity getFileEntity(OCFile file) { + if (file == null) { + return null; + } + + return fileDao.getFileById(file.getFileId()); + } + + public void updateFileEntity(@NonNull FileEntity entity) { + fileDao.update(entity); + } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java b/app/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java index 86b05fb..44f768c 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java @@ -7,7 +7,7 @@ * Copyright (C) 2017 Nextcloud * Copyright (C) 2018 Andy Scherzinger * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index aec6fda..43aa27f 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Mario Danic * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; @@ -32,24 +32,28 @@ public class FilesystemDataProvider { static private final String TAG = FilesystemDataProvider.class.getSimpleName(); - private ContentResolver contentResolver; + private final ContentResolver contentResolver; public FilesystemDataProvider(ContentResolver contentResolver) { if (contentResolver == null) { + Log_OC.e(TAG, "couldn't be able constructed, contentResolver is null"); throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver"); } this.contentResolver = contentResolver; } public int deleteAllEntriesForSyncedFolder(String syncedFolderId) { + Log_OC.d(TAG, "deleteAllEntriesForSyncedFolder called, ID: " + syncedFolderId); + return contentResolver.delete( ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", - new String[]{syncedFolderId} - ); + new String[]{syncedFolderId}); } public void updateFilesystemFileAsSentForUpload(String path, String syncedFolderId) { + Log_OC.d(TAG, "updateFilesystemFileAsSentForUpload called, path: " + path + " ID: " + syncedFolderId); + ContentValues cv = new ContentValues(); cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 1); @@ -62,7 +66,67 @@ public class FilesystemDataProvider { ); } + public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, SyncedFolder syncedFolder) { + Log_OC.d(TAG, "storeOrUpdateFileValue called, localPath: " + localPath + " ID: " + syncedFolder.getId()); + + // takes multiple milliseconds to query data from database (around 75% of execution time) (6ms) + FileSystemDataSet data = getFilesystemDataSet(localPath, syncedFolder); + + int isFolderValue = 0; + if (isFolder) { + isFolderValue = 1; + } + + ContentValues cv = new ContentValues(); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, System.currentTimeMillis()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, modifiedAt); + + if (data == null) { + Log_OC.d(TAG, "storeOrUpdateFileValue data is null"); + + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, isFolderValue); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, Boolean.FALSE); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID, syncedFolder.getId()); + + long newCrc32 = getFileChecksum(localPath); + if (newCrc32 != -1) { + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32, Long.toString(newCrc32)); + } + + Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv); + + if (result == null) { + Log_OC.e(TAG, "Failed to insert filesystem data with local path: " + localPath); + } + } else { + Log_OC.d(TAG, "storeOrUpdateFileValue data is not null"); + + if (data.getModifiedAt() != modifiedAt) { + long newCrc32 = getFileChecksum(localPath); + if (data.getCrc32() == null || (newCrc32 != -1 && !data.getCrc32().equals(Long.toString(newCrc32)))) { + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32, Long.toString(newCrc32)); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 0); + } + } + + // updating data takes multiple milliseconds (around 25% of exec time) (2 ms) + int result = contentResolver.update( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + cv, + ProviderMeta.ProviderTableMeta._ID + "=?", + new String[]{String.valueOf(data.getId())} + ); + + if (result == 0) { + Log_OC.e(TAG, "Failed to update filesystem data with local path: " + localPath); + } + } + } + public Set getFilesForUpload(String localPath, String syncedFolderId) { + Log_OC.d(TAG, "getFilesForUpload called, localPath: " + localPath + " ID: " + syncedFolderId); + Set localPathsToUpload = new HashSet<>(); String likeParam = localPath + "%"; @@ -87,132 +151,87 @@ public class FilesystemDataProvider { } else { File file = new File(value); if (!file.exists()) { - Log_OC.d(TAG, "Ignoring file for upload (doesn't exist): " + value); + Log_OC.w(TAG, "Ignoring file for upload (doesn't exist): " + value); } else if (!SyncedFolderUtils.isQualifiedFolder(file.getParent())) { - Log_OC.d(TAG, "Ignoring file for upload (unqualified folder): " + value); + Log_OC.w(TAG, "Ignoring file for upload (unqualified folder): " + value); } else if (!SyncedFolderUtils.isFileNameQualifiedForAutoUpload(file.getName())) { - Log_OC.d(TAG, "Ignoring file for upload (unqualified file): " + value); + Log_OC.w(TAG, "Ignoring file for upload (unqualified file): " + value); } else { + Log_OC.d(TAG, "adding path to the localPathsToUpload: " + value); localPathsToUpload.add(value); } } } while (cursor.moveToNext()); + } else { + Log_OC.w(TAG, "cursor cannot move"); } cursor.close(); + } else { + Log_OC.e(TAG, "getFilesForUpload called, cursor is null"); } return localPathsToUpload; } - public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, SyncedFolder syncedFolder) { - - // takes multiple milliseconds to query data from database (around 75% of execution time) (6ms) - FileSystemDataSet data = getFilesystemDataSet(localPath, syncedFolder); - - int isFolderValue = 0; - if (isFolder) { - isFolderValue = 1; - } - - ContentValues cv = new ContentValues(); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, System.currentTimeMillis()); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, modifiedAt); - - if (data == null) { - - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, isFolderValue); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, Boolean.FALSE); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID, syncedFolder.getId()); - - long newCrc32 = getFileChecksum(localPath); - if (newCrc32 != -1) { - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32, Long.toString(newCrc32)); - } - - Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv); - - if (result == null) { - Log_OC.v(TAG, "Failed to insert filesystem data with local path: " + localPath); - } - } else { - - if (data.getModifiedAt() != modifiedAt) { - long newCrc32 = getFileChecksum(localPath); - if (data.getCrc32() == null || (newCrc32 != -1 && !data.getCrc32().equals(Long.toString(newCrc32)))) { - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32, Long.toString(newCrc32)); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 0); - } - } - - // updating data takes multiple milliseconds (around 25% of exec time) (2 ms) - int result = contentResolver.update( - ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - cv, - ProviderMeta.ProviderTableMeta._ID + "=?", - new String[]{String.valueOf(data.getId())} - ); - - if (result == 0) { - Log_OC.v(TAG, "Failed to update filesystem data with local path: " + localPath); - } - } - } - private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFolder syncedFolder) { + Log_OC.d(TAG, "getFilesForUpload called, localPath: " + localPathParam + " ID: " + syncedFolder.getId()); - Cursor cursor = contentResolver.query( + String[] projection = { + ProviderMeta.ProviderTableMeta._ID, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, + ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32 + }; + + String selection = ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? AND " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?"; + String[] selectionArgs = { localPathParam, String.valueOf(syncedFolder.getId()) }; + + try (Cursor cursor = contentResolver.query( ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - null, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? and " + - ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?", - new String[]{localPathParam, Long.toString(syncedFolder.getId())}, + projection, + selection, + selectionArgs, null - ); - - FileSystemDataSet dataSet = null; - if (cursor != null) { - if (cursor.moveToFirst()) { + )) { + if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta._ID)); + if (id == -1) { + Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); + return null; + } + String localPath = cursor.getString(cursor.getColumnIndexOrThrow( ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); long modifiedAt = cursor.getLong(cursor.getColumnIndexOrThrow( ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)); - boolean isFolder = false; - if (cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0) { - isFolder = true; - } - long foundAt = cursor.getLong(cursor.getColumnIndexOrThrow(ProviderMeta. - ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); + boolean isFolder = cursor.getInt(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0; + long foundAt = cursor.getLong(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); + boolean isSentForUpload = cursor.getInt(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0; + String crc32 = cursor.getString(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32)); - boolean isSentForUpload = false; - if (cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0) { - isSentForUpload = true; - } - - String crc32 = cursor.getString(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32)); - - if (id == -1) { - Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); - } else { - dataSet = new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt, - syncedFolder.getId(), crc32); - } + return new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt, + syncedFolder.getId(), crc32); } - cursor.close(); - } else { - Log_OC.e(TAG, "DB error restoring arbitrary values."); + } catch (Exception e) { + Log_OC.e(TAG, "DB error restoring arbitrary values.", e); } - return dataSet; + return null; } private long getFileChecksum(String filepath) { - try (InputStream inputStream = new BufferedInputStream(new FileInputStream(filepath))) { + try (FileInputStream fileInputStream = new FileInputStream(filepath); + InputStream inputStream = new BufferedInputStream(fileInputStream)) { CRC32 crc = new CRC32(); byte[] buf = new byte[1024 * 64]; int size; diff --git a/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt b/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt index 9e65d6f..46bb5ee 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel @@ -18,14 +18,13 @@ import androidx.annotation.RequiresApi * This wrapper is designed for compatibility on those versions. */ enum class ForegroundServiceType { - DataSync, MediaPlayback; + DataSync, + MediaPlayback; @RequiresApi(Build.VERSION_CODES.Q) - fun getId(): Int { - return if (this == DataSync) { - ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC - } else { - ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK - } + fun getId(): Int = if (this == DataSync) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + } else { + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt b/app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt index 363d6a4..44cb317 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/main/java/com/owncloud/android/datamodel/GalleryRow.kt b/app/src/main/java/com/owncloud/android/datamodel/GalleryRow.kt index 4e7a7aa..febd3aa 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/GalleryRow.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/GalleryRow.kt @@ -1,14 +1,18 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel +import com.nextcloud.utils.OCFileUtils + data class GalleryRow(val files: List, val defaultHeight: Int, val defaultWidth: Int) { - fun getMaxHeight(): Float { - return files.map { it.imageDimension?.height ?: defaultHeight.toFloat() }.maxOrNull() ?: 0f - } + fun getMaxHeight(): Float = files.maxOfOrNull { + OCFileUtils.getImageSize(it, defaultHeight.toFloat()).second.toFloat() + } ?: 0f + fun calculateHashCode(): Long = files.sumOf { it.hashCode() }.toLong() } diff --git a/app/src/main/java/com/owncloud/android/datamodel/MediaFolder.kt b/app/src/main/java/com/owncloud/android/datamodel/MediaFolder.kt index 028d28f..0ebb02d 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/MediaFolder.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/MediaFolder.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Nextcloud - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/main/java/com/owncloud/android/datamodel/MediaFolderType.kt b/app/src/main/java/com/owncloud/android/datamodel/MediaFolderType.kt index b5d2dcc..8b98a18 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/MediaFolderType.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/MediaFolderType.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel @@ -12,7 +12,9 @@ import android.util.SparseArray * Types of media folder. */ enum class MediaFolderType(@JvmField val id: Int) { - CUSTOM(0), IMAGE(1), VIDEO(2); + CUSTOM(0), + IMAGE(1), + VIDEO(2); companion object { private val reverseMap = SparseArray(3) @@ -24,8 +26,6 @@ enum class MediaFolderType(@JvmField val id: Int) { } @JvmStatic - fun getById(id: Int?): MediaFolderType { - return reverseMap[id!!] - } + fun getById(id: Int?): MediaFolderType = reverseMap[id!!] } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/MediaFoldersModel.kt b/app/src/main/java/com/owncloud/android/datamodel/MediaFoldersModel.kt index b4d95f1..dc08c09 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/MediaFoldersModel.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/MediaFoldersModel.kt @@ -6,7 +6,7 @@ * Copyright (C) 2018 Mario Danic * Copyright (C) 2018 Andy Scherzinger * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/main/java/com/owncloud/android/datamodel/MediaProvider.java b/app/src/main/java/com/owncloud/android/datamodel/MediaProvider.java index 074d6c4..c3dd31c 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/MediaProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/MediaProvider.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Nextcloud - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java index c6bba5a..11455a2 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Andy Scherzinger @@ -9,7 +9,7 @@ * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2012-2016 David A. Velasco * SPDX-FileCopyrightText: 2012 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datamodel; @@ -20,36 +20,45 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import com.owncloud.android.BuildConfig; +import com.nextcloud.utils.BuildHelper; +import com.nextcloud.utils.extensions.StringExtensionsKt; import com.owncloud.android.R; import com.owncloud.android.lib.common.network.WebdavEntry; -import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.model.FileLockType; import com.owncloud.android.lib.resources.files.model.GeoLocation; import com.owncloud.android.lib.resources.files.model.ImageDimension; import com.owncloud.android.lib.resources.files.model.ServerFileInterface; import com.owncloud.android.lib.resources.shares.ShareeUser; +import com.owncloud.android.lib.resources.tags.Tag; import com.owncloud.android.utils.MimeType; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import androidx.core.content.FileProvider; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import third_parties.daveKoeller.AlphanumComparator; public class OCFile implements Parcelable, Comparable, ServerFileInterface { - private final static String PERMISSION_SHARED_WITH_ME = "S"; - @VisibleForTesting public final static String PERMISSION_CAN_RESHARE = "R"; - private final static String PERMISSION_CAN_WRITE = "CK"; - private final static String PERMISSION_GROUPFOLDER = "M"; + private final static String PERMISSION_SHARED = "S"; + private final static String PERMISSION_MOUNTED = "M"; + private final static String PERMISSION_CAN_CREATE_FILE_INSIDE_FOLDER = "C"; + private final static String PERMISSION_CAN_CREATE_FOLDER_INSIDE_FOLDER = "K"; + private final static String PERMISSION_CAN_READ = "G"; + private final static String PERMISSION_CAN_WRITE = "W"; + private final static String PERMISSION_CAN_DELETE_OR_LEAVE_SHARE = "D"; + private final static String PERMISSION_CAN_RENAME = "N"; + private final static String PERMISSION_CAN_MOVE = "V"; + private final static String PERMISSION_CAN_CREATE_FILE_AND_FOLDER = PERMISSION_CAN_CREATE_FILE_INSIDE_FOLDER + PERMISSION_CAN_CREATE_FOLDER_INSIDE_FOLDER; + + private final static int MAX_FILE_SIZE_FOR_IMMEDIATE_PREVIEW_BYTES = 1024000; public static final String PATH_SEPARATOR = "/"; public static final String ROOT_PATH = PATH_SEPARATOR; @@ -116,7 +125,14 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa private long e2eCounter = -1; @Nullable private GeoLocation geolocation; - private List tags = new ArrayList<>(); + private List tags = new ArrayList<>(); + private Long internalFolderSyncTimestamp = -1L; + private String internalFolderSyncResult = ""; + + // region Recommend files variables + private boolean recommendedFile = false; + private String reason = ""; + // endregion /** * URI to the local path of the file contents, if stored in the device; cached after first call to @@ -157,6 +173,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa fileId = source.readLong(); parentId = source.readLong(); fileLength = source.readLong(); + uploadTimestamp = source.readLong(); creationTimestamp = source.readLong(); modificationTimestamp = source.readLong(); modificationTimestampAtLastSyncForData = source.readLong(); @@ -202,6 +219,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa dest.writeLong(fileId); dest.writeLong(parentId); dest.writeLong(fileLength); + dest.writeLong(uploadTimestamp); dest.writeLong(creationTimestamp); dest.writeLong(modificationTimestamp); dest.writeLong(modificationTimestampAtLastSyncForData); @@ -380,26 +398,11 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return localUri; } - - public Uri getLegacyExposedFileUri() { - if (TextUtils.isEmpty(localPath)) { - return null; - } - - if (exposedFileUri == null) { - return Uri.parse(ContentResolver.SCHEME_FILE + "://" + WebdavUtils.encodePath(localPath)); - } - - return exposedFileUri; - - } - /* - Partly disabled because not all apps understand paths that we get via this method for now - */ public Uri getExposedFileUri(Context context) { if (TextUtils.isEmpty(localPath)) { return null; } + if (exposedFileUri == null) { try { exposedFileUri = FileProvider.getUriForFile( @@ -407,9 +410,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa context.getString(R.string.file_provider_authority), new File(localPath)); } catch (IllegalArgumentException ex) { - // Could not share file using FileProvider URI scheme. - // Fall back to legacy URI parsing. - getLegacyExposedFileUri(); + Log_OC.d(TAG, "Given File is outside the paths supported by the provider"); } } @@ -496,6 +497,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa localPath = null; mimeType = null; fileLength = 0; + uploadTimestamp = 0; creationTimestamp = 0; modificationTimestamp = 0; modificationTimestampAtLastSyncForData = 0; @@ -577,7 +579,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa @Override public int hashCode() { - return 31 * (int) (fileId ^ (fileId >>> 32)) + (int) (parentId ^ (parentId >>> 32)); + return Objects.hash(fileId,parentId); } @NonNull @@ -639,27 +641,67 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa } public boolean isSharedWithMe() { - String permissions = getPermissions(); - return permissions != null && permissions.contains(PERMISSION_SHARED_WITH_ME); + return hasPermission(PERMISSION_SHARED); } public boolean canReshare() { - String permissions = getPermissions(); - return permissions != null && permissions.contains(PERMISSION_CAN_RESHARE); + return hasPermission(PERMISSION_CAN_RESHARE); + } + + public boolean canCreateFileAndFolder() { + return hasPermission(PERMISSION_CAN_CREATE_FILE_AND_FOLDER); + } + + public boolean mounted() { + return hasPermission(PERMISSION_MOUNTED); + } + + public boolean canRead() { + return hasPermission(PERMISSION_CAN_READ); + } + + public boolean canCreateFileInsideFolder() { + return hasPermission(PERMISSION_CAN_CREATE_FILE_INSIDE_FOLDER); + } + + public boolean canCreateFolderInsideFolder() { + return hasPermission(PERMISSION_CAN_CREATE_FOLDER_INSIDE_FOLDER); + } + + /** + * Determines whether the current account has the ability to delete the file or leave the share. + * + *

    + * - If the file is shared with the current account (i.e., the user is the recipient), + * the user cannot delete the file itself but can leave the shared file. + *

    + * - If the file is belongs to the current user. User can delete the file. + * + * @return true if the user is allowed to either delete or leave the share; false otherwise. + */ + public boolean canDeleteOrLeaveShare() { + return hasPermission(PERMISSION_CAN_DELETE_OR_LEAVE_SHARE); + } + + public boolean canRename() { + return hasPermission(PERMISSION_CAN_RENAME); } public boolean canWrite() { - String permissions = getPermissions(); - return permissions != null && permissions.contains(PERMISSION_CAN_WRITE); + return hasPermission(PERMISSION_CAN_WRITE); } - public boolean isGroupFolder() { + public boolean canMove() { + return hasPermission(PERMISSION_CAN_MOVE); + } + + private boolean hasPermission(String permission) { String permissions = getPermissions(); - return permissions != null && permissions.contains(PERMISSION_GROUPFOLDER); + return permissions != null && permissions.contains(permission); } public Integer getFileOverlayIconId(boolean isAutoUploadFolder) { - if (WebdavEntry.MountType.GROUP == mountType || isGroupFolder()) { + if (WebdavEntry.MountType.GROUP == mountType || mounted()) { return R.drawable.ic_folder_overlay_account_group; } else if (sharedViaLink && !encrypted) { return R.drawable.ic_folder_overlay_link; @@ -678,7 +720,7 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa } } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<>() { @Override public OCFile createFromParcel(Parcel source) { @@ -706,6 +748,10 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return this.fileLength; } + public boolean isFileEligibleForImmediatePreview() { + return fileLength <= MAX_FILE_SIZE_FOR_IMMEDIATE_PREVIEW_BYTES; + } + public long getCreationTimestamp() { return this.creationTimestamp; } @@ -717,10 +763,6 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return this.modificationTimestamp; } - public long getUploadTimestamp() { - return this.uploadTimestamp; - } - public long getModificationTimestampAtLastSyncForData() { return this.modificationTimestampAtLastSyncForData; } @@ -753,10 +795,18 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return this.etagOnServer; } + public boolean isEtagChanged() { + return !StringExtensionsKt.isNotBlankAndEquals(getEtag(), getEtagOnServer()); + } + public boolean isSharedViaLink() { return this.sharedViaLink; } + public boolean isShared() { + return isSharedViaLink() || isSharedWithSharee() || isSharedWithMe() || !sharees.isEmpty(); + } + public String getPermissions() { return this.permissions; } @@ -773,6 +823,14 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return this.downloading; } + public boolean isRootDirectory() { + return ROOT_PATH.equals(decryptedRemotePath); + } + + public boolean isOfflineOperation() { + return getRemoteId() == null; + } + public String getEtagInConflict() { return this.etagInConflict; } @@ -1032,11 +1090,11 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa return geolocation; } - public List getTags() { + public List getTags() { return tags; } - public void setTags(List tags) { + public void setTags(List tags) { this.tags = tags; } @@ -1045,18 +1103,67 @@ public class OCFile implements Parcelable, Comparable, ServerFileInterfa } public void setE2eCounter(@Nullable Long e2eCounter) { - if (e2eCounter == null) { - this.e2eCounter = -1; - } else { - this.e2eCounter = e2eCounter; + this.e2eCounter = Objects.requireNonNullElse(e2eCounter, -1L); + } + + public boolean isInternalFolderSync() { + if (internalFolderSyncTimestamp == null) { + return false; } + + return internalFolderSyncTimestamp >= 0; + } + + public Long getInternalFolderSyncTimestamp() { + return Objects.requireNonNullElse(internalFolderSyncTimestamp, -1L); + } + + public void setInternalFolderSyncTimestamp(Long internalFolderSyncTimestamp) { + this.internalFolderSyncTimestamp = internalFolderSyncTimestamp; + } + + public String getInternalFolderSyncResult() { + return internalFolderSyncResult; + } + + public void setInternalFolderSyncResult(String internalFolderSyncResult) { + this.internalFolderSyncResult = internalFolderSyncResult; } public boolean isAPKorAAB() { - if ("gplay".equals(BuildConfig.FLAVOR)) { + if (BuildHelper.INSTANCE.isFlavourGPlay()) { return getFileName().endsWith(".apk") || getFileName().endsWith(".aab"); } else { return false; } } + + public long getUploadTimestamp() { + return uploadTimestamp; + } + + public void setUploadTimestamp(long uploadTimestamp) { + this.uploadTimestamp = uploadTimestamp; + } + + public boolean exists() { + final String storagePath = getStoragePath(); + return storagePath != null && new File(storagePath).exists(); + } + + public void setReason(String value) { + reason = value; + } + + public String getReason() { + return reason; + } + + public void setIsRecommendedFile(boolean value) { + recommendedFile = value; + } + + public boolean isRecommendedFile() { + return recommendedFile; + } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/PushConfigurationState.java b/app/src/main/java/com/owncloud/android/datamodel/PushConfigurationState.java index d24989f..617f703 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/PushConfigurationState.java +++ b/app/src/main/java/com/owncloud/android/datamodel/PushConfigurationState.java @@ -6,7 +6,7 @@ * Copyright (C) 2017 Mario Danic * Copyright (C) 2018 Andy Scherzinger * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/main/java/com/owncloud/android/datamodel/QuickPermissionModel.kt b/app/src/main/java/com/owncloud/android/datamodel/QuickPermissionModel.kt deleted file mode 100644 index 8116311..0000000 --- a/app/src/main/java/com/owncloud/android/datamodel/QuickPermissionModel.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author TSI-mc - * Copyright (C) 2021 TSI-mc - * Copyright (C) 2021 Nextcloud GmbH - * - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.owncloud.android.datamodel - -data class QuickPermissionModel(val permissionName: String, val isSelected: Boolean) diff --git a/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt b/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt index 100d726..b64be49 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel @@ -15,7 +15,5 @@ enum class ReceiverFlag { NotExported; @RequiresApi(Build.VERSION_CODES.TIRAMISU) - fun getId(): Int { - return Context.RECEIVER_NOT_EXPORTED - } + fun getId(): Int = Context.RECEIVER_NOT_EXPORTED } diff --git a/app/src/main/java/com/owncloud/android/datamodel/SharesType.kt b/app/src/main/java/com/owncloud/android/datamodel/SharesType.kt new file mode 100644 index 0000000..fb58a83 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/datamodel/SharesType.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Tobias Kaminsky + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.datamodel + +enum class SharesType { + INTERNAL, + EXTERNAL +} diff --git a/app/src/main/java/com/owncloud/android/datamodel/SignatureVerification.kt b/app/src/main/java/com/owncloud/android/datamodel/SignatureVerification.kt index 96f3b41..a021746 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SignatureVerification.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/SignatureVerification.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2022 Unpublished - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 3e5b8fa..ee2cdda 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -5,14 +5,16 @@ * Copyright (C) 2016 Tobias Kaminsky * Copyright (C) 2016 Nextcloud * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; import com.nextcloud.client.preferences.SubFolderRule; import com.owncloud.android.files.services.NameCollisionPolicy; +import com.owncloud.android.utils.MimeTypeUtil; +import java.io.File; import java.io.Serializable; /** @@ -274,8 +276,12 @@ public class SyncedFolder implements Serializable, Cloneable { this.excludeHidden = excludeHidden; } - public boolean containsFile(String filePath){ - return filePath.contains(localPath); + public boolean containsTypedFile(String filePath){ + boolean isCorrectMediaType = + (getType() == MediaFolderType.IMAGE && MimeTypeUtil.isImage(new File(filePath))) || + (getType() == MediaFolderType.VIDEO && MimeTypeUtil.isVideo(new File(filePath))) || + getType() == MediaFolderType.CUSTOM; + return filePath.contains(localPath) && isCorrectMediaType; } public long getLastScanTimestampMs() { return lastScanTimestampMs; } diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 2bef272..cbbf445 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Nextcloud - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 5147889..fbcba50 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Nextcloud - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; @@ -27,6 +27,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Observable; +import javax.annotation.Nullable; + import androidx.annotation.NonNull; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -210,6 +212,29 @@ public class SyncedFolderProvider extends Observable { } + @Nullable + public SyncedFolder getSyncedFolderByID(Long syncedFolderID) { + SyncedFolder result = null; + Cursor cursor = mContentResolver.query( + ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, + null, + ProviderMeta.ProviderTableMeta._ID + " =? ", + new String[]{syncedFolderID.toString()}, + null + ); + + if (cursor != null && cursor.getCount() == 1 && cursor.moveToFirst()) { + result = createSyncedFolderFromCursor(cursor); + } + + if (cursor != null) { + cursor.close(); + } + + return result; + + } + /** * Delete all synced folders for an account * @@ -228,12 +253,12 @@ public class SyncedFolderProvider extends Observable { * * @param id for the synced folder. */ - private int deleteSyncFolderWithId(long id) { - return mContentResolver.delete( - ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, - ProviderMeta.ProviderTableMeta._ID + " = ?", - new String[]{String.valueOf(id)} - ); + private void deleteSyncFolderWithId(long id) { + mContentResolver.delete( + ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, + ProviderMeta.ProviderTableMeta._ID + " = ?", + new String[]{String.valueOf(id)} + ); } @@ -444,9 +469,6 @@ public class SyncedFolderProvider extends Observable { } else { if (cursor == null) { Log_OC.e(TAG, "Sync folder db cursor for remote path = " + remotePath + " in NULL."); - } else { - Log_OC.e(TAG, cursor.getCount() + " items for remote path = " + remotePath - + " available in sync folder db. Expected 1 or greater than 1. Failed to update sync folder db."); } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/Template.kt b/app/src/main/java/com/owncloud/android/datamodel/Template.kt index b345256..3f3beb6 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/Template.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/Template.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel @@ -14,15 +14,13 @@ import kotlinx.parcelize.Parcelize * Template for creating a file from it via RichDocuments app */ @Parcelize -data class Template( - val id: Long, - val name: String, - val thumbnailLink: String, - val type: Type, - val extension: String -) : Parcelable { +data class Template(val id: Long, val name: String, val thumbnailLink: String, val type: Type, val extension: String) : + Parcelable { enum class Type { - DOCUMENT, SPREADSHEET, PRESENTATION, UNKNOWN; + DOCUMENT, + SPREADSHEET, + PRESENTATION, + UNKNOWN; companion object { @JvmStatic diff --git a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index f4b62ac..00ee407 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2014 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datamodel; @@ -36,6 +36,8 @@ import android.widget.ImageView; import com.nextcloud.client.account.User; import com.nextcloud.client.network.ConnectivityService; +import com.nextcloud.utils.BitmapExtensionsKt; +import com.nextcloud.utils.extensions.ThumbnailsCacheManagerExtensionsKt; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.lib.common.OwnCloudAccount; @@ -62,17 +64,22 @@ import org.apache.commons.httpclient.methods.GetMethod; import java.io.File; import java.io.FileNotFoundException; -import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.List; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import static com.nextcloud.utils.extensions.ThumbnailsCacheManagerExtensionsKt.getExifOrientation; + /** * Manager for concurrent access to thumbnails cache. */ @@ -98,6 +105,8 @@ public final class ThumbnailsCacheManager { private static final CompressFormat mCompressFormat = CompressFormat.JPEG; private static final int mCompressQuality = 70; private static OwnCloudClient mClient; + private static final int THUMBNAIL_SIZE_IN_KB = 512; + private static final int RESIZED_IMAGE_SIZE_IN_KB = 10240; public static final Bitmap mDefaultImg = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(), R.drawable.file_image); @@ -108,9 +117,10 @@ public final class ThumbnailsCacheManager { private ThumbnailsCacheManager() { } - public static class InitDiskCacheTask extends AsyncTask { - @Override - protected Void doInBackground(File... params) { + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); + + public static void initDiskCacheAsync() { + executor.execute(() -> { synchronized (mThumbnailsDiskCacheLock) { mThumbnailCacheStarting = true; @@ -147,8 +157,7 @@ public final class ThumbnailsCacheManager { mThumbnailCacheStarting = false; // Finished initialization mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads } - return null; - } + }); } /** @@ -188,7 +197,8 @@ public final class ThumbnailsCacheManager { Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH); // Rotate image, obeying exif tag - thumbnail = BitmapUtils.rotateImage(thumbnail,path); + int orientation = getExifOrientation(path); + thumbnail = BitmapExtensionsKt.rotateBitmapViaExif(thumbnail, orientation); // Add thumbnail to cache // do not overwrite any pre-existing image @@ -199,11 +209,55 @@ public final class ThumbnailsCacheManager { return thumbnail; } + public static void removeFromCache(@Nullable OCFile file) { + if (file == null) { + return; + } + + final var keys = new String[] { PREFIX_RESIZED_IMAGE + file.getRemoteId(), PREFIX_THUMBNAIL + file.getRemoteId() }; + + synchronized (mThumbnailsDiskCacheLock) { + if (mThumbnailCache == null) { + return; + } + + for (String key: keys) { + mThumbnailCache.removeKey(key); + } + } + } + public static void addBitmapToCache(String key, Bitmap bitmap) { synchronized (mThumbnailsDiskCacheLock) { - if (mThumbnailCache != null) { - mThumbnailCache.put(key, bitmap); + if (mThumbnailCache == null) { + return; } + + // Check if the bitmap is already cached + Bitmap cachedBitmap = mThumbnailCache.getBitmap(key); + if (cachedBitmap == null) { + cachedBitmap = mThumbnailCache.getScaledBitmap(key, bitmap.getWidth(), bitmap.getHeight()); + } + + if (cachedBitmap != null && BitmapExtensionsKt.allocationKilobyte(cachedBitmap) <= THUMBNAIL_SIZE_IN_KB) { + Log_OC.d(TAG, "Cached version is already within size limits, no need to scale: " + key); + return; + } + + // do not scale down resized images + int size; + if (key.startsWith("r")) { + size = RESIZED_IMAGE_SIZE_IN_KB; + } else { + size = THUMBNAIL_SIZE_IN_KB; + } + + if (BitmapExtensionsKt.allocationKilobyte(bitmap) > size) { + Log_OC.d(TAG, "Scaling bitmap before caching: " + key); + bitmap = BitmapExtensionsKt.scaleUntil(bitmap, size); + } + + mThumbnailCache.put(key, bitmap); } } @@ -250,10 +304,10 @@ public final class ThumbnailsCacheManager { private final FileDataStorageManager storageManager; private final WeakReference imageViewReference; private OCFile file; - private String imageKey; + private final String imageKey; private GalleryListener listener; - private List asyncTasks; - private int backgroundColor; + private final List asyncTasks; + private final int backgroundColor; private boolean newImage = false; public GalleryImageGenerationTask( @@ -262,8 +316,7 @@ public final class ThumbnailsCacheManager { FileDataStorageManager storageManager, List asyncTasks, String imageKey, - int backgroundColor - ) { + int backgroundColor) { this.user = user; this.storageManager = storageManager; imageViewReference = new WeakReference<>(imageView); @@ -272,72 +325,98 @@ public final class ThumbnailsCacheManager { this.backgroundColor = backgroundColor; } - public void setListener(GalleryImageGenerationTask.GalleryListener listener) { + public void setListener(GalleryListener listener) { this.listener = listener; } - public String getImageKey() { - return imageKey; - } - @Override protected Bitmap doInBackground(Object... params) { Bitmap thumbnail; - file = (OCFile) params[0]; - - - if (file.getRemoteId() != null && file.isPreviewAvailable()) { - // Thumbnail in cache? - thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + file.getRemoteId() - ); - - if (thumbnail != null && !file.isUpdateThumbnailNeeded()) { - Float size = (float) ThumbnailsCacheManager.getThumbnailDimension(); - - // resized dimensions - ImageDimension imageDimension = file.getImageDimension(); - if (imageDimension == null || - imageDimension.getWidth() != size || - imageDimension.getHeight() != size) { - file.setImageDimension(new ImageDimension(thumbnail.getWidth(), thumbnail.getHeight())); - storageManager.saveFile(file); - } - - if (MimeTypeUtil.isVideo(file)) { - return ThumbnailsCacheManager.addVideoOverlay(thumbnail, MainApp.getAppContext()); - } else { - return thumbnail; - } - } else { - try { - mClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(user.toOwnCloudAccount(), - MainApp.getAppContext()); - - thumbnail = doResizedImageInBackground(file, storageManager); - newImage = true; - - if (MimeTypeUtil.isVideo(file) && thumbnail != null) { - thumbnail = addVideoOverlay(thumbnail, MainApp.getAppContext()); - } - - } catch (OutOfMemoryError oome) { - Log_OC.e(TAG, "Out of memory"); - } catch (Throwable t) { - // the app should never break due to a problem with thumbnails - Log_OC.e(TAG, "Generation of gallery image for " + file + " failed", t); - } - - return thumbnail; - } + if (params == null || params.length == 0 || !(params[0] instanceof OCFile)) { + Log_OC.d(TAG, "Downloaded file is null or is not an instance of OCFile"); + return null; } + file = (OCFile) params[0]; + + if (file.getRemoteId() != null || file.isPreviewAvailable()) { + // Thumbnail in cache? + thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + file.getRemoteId()); + + if (thumbnail != null && !file.isUpdateThumbnailNeeded()) + return getThumbnailFromCache(thumbnail); + + return getThumbnailFromServerAndAddToCache(thumbnail); + } + + Log_OC.d(TAG, "File cannot be previewed"); return null; } + @Nullable + private Bitmap getThumbnailFromServerAndAddToCache(Bitmap thumbnail) { + try { + mClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(user.toOwnCloudAccount(), + MainApp.getAppContext()); + + thumbnail = doResizedImageInBackground(file, storageManager); + newImage = true; + + if (MimeTypeUtil.isVideo(file) && thumbnail != null) { + thumbnail = addVideoOverlay(thumbnail, MainApp.getAppContext()); + } + + } catch (OutOfMemoryError oome) { + Log_OC.e(TAG, "Out of memory"); + } catch (Throwable t) { + // the app should never break due to a problem with thumbnails + Log_OC.e(TAG, "Generation of gallery image for " + file + " failed", t); + } + + return thumbnail; + } + + private Bitmap getThumbnailFromCache(Bitmap thumbnail) { + float size = (float) ThumbnailsCacheManager.getThumbnailDimension(); + + // resized dimensions + ImageDimension imageDimension = file.getImageDimension(); + if (imageDimension == null || + imageDimension.getWidth() != size || + imageDimension.getHeight() != size) { + file.setImageDimension(new ImageDimension(thumbnail.getWidth(), thumbnail.getHeight())); + storageManager.saveFile(file); + } + + Bitmap result = thumbnail; + if (MimeTypeUtil.isVideo(file)) { + result = ThumbnailsCacheManager.addVideoOverlay(thumbnail, MainApp.getAppContext()); + } + + if (BitmapExtensionsKt.allocationKilobyte(thumbnail) > THUMBNAIL_SIZE_IN_KB) { + result = getScaledThumbnailAfterSave(result); + } + + return result; + } + + private Bitmap getScaledThumbnailAfterSave(Bitmap thumbnail) { + Bitmap result = BitmapExtensionsKt.scaleUntil(thumbnail, THUMBNAIL_SIZE_IN_KB); + + synchronized (mThumbnailsDiskCacheLock) { + if (mThumbnailCache != null) { + Log_OC.d(TAG, "Scaling bitmap before caching: " + imageKey); + mThumbnailCache.put(imageKey, result); + } + } + + return result; + } + protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null && imageViewReference != null) { + if (bitmap != null && imageViewReference.get() != null) { final ImageView imageView = imageViewReference.get(); final GalleryImageGenerationTask bitmapWorkerTask = getGalleryImageGenerationTask(imageView); @@ -434,7 +513,7 @@ public final class ThumbnailsCacheManager { } protected void onPostExecute(Bitmap bitmap) { - if (imageViewReference != null) { + if (imageViewReference.get() != null) { final ImageView imageView = imageViewReference.get(); final FrameLayout frameLayout = frameLayoutReference.get(); @@ -462,7 +541,7 @@ public final class ThumbnailsCacheManager { } } else { if (fileFragment instanceof PreviewImageFragment) { - ((PreviewImageFragment) fileFragment).setErrorPreviewMessage(); + ((PreviewImageFragment) fileFragment).handleUnsupportedImage(); } } }).start(); @@ -657,8 +736,7 @@ public final class ThumbnailsCacheManager { int pxH; pxW = pxH = getThumbnailDimension(); - if (file instanceof OCFile) { - OCFile ocFile = (OCFile) file; + if (file instanceof OCFile ocFile) { if (ocFile.isDown()) { Bitmap bitmap; if (MimeTypeUtil.isVideo(ocFile)) { @@ -761,19 +839,14 @@ public final class ThumbnailsCacheManager { private int getThumbnailDimension() { // Converts dp to pixel Resources r = MainApp.getAppContext().getResources(); - Double d = Math.pow(2, Math.floor(Math.log(r.getDimension(R.dimen.file_icon_size_grid)) / Math.log(2))); - return d.intValue(); + double d = Math.pow(2, Math.floor(Math.log(r.getDimension(R.dimen.file_icon_size_grid)) / Math.log(2))); + return (int) d; } private Bitmap doFileInBackground() { File file = (File)mFile; - final String imageKey; - if (mImageKey != null) { - imageKey = mImageKey; - } else { - imageKey = String.valueOf(file.hashCode()); - } + final String imageKey = Objects.requireNonNullElseGet(mImageKey, () -> String.valueOf(file.hashCode())); // local file should always generate a thumbnail mImageKey = PREFIX_THUMBNAIL + mImageKey; @@ -885,13 +958,7 @@ public final class ThumbnailsCacheManager { } private Bitmap doFileInBackground(File file, Type type) { - final String imageKey; - - if (mImageKey != null) { - imageKey = mImageKey; - } else { - imageKey = String.valueOf(file.hashCode()); - } + final String imageKey = Objects.requireNonNullElseGet(mImageKey, () -> String.valueOf(file.hashCode())); // Check disk cache in background thread Bitmap thumbnail = getBitmapFromDiskCache(imageKey); @@ -908,20 +975,12 @@ public final class ThumbnailsCacheManager { thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px, px); } } else if (Type.VIDEO == type) { - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - try { + try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) { retriever.setDataSource(file.getAbsolutePath()); thumbnail = retriever.getFrameAtTime(-1); } catch (Exception ex) { // can't create a bitmap Log_OC.w(TAG, "Failed to create bitmap from video " + file.getAbsolutePath()); - } finally { - try { - retriever.release(); - } catch (RuntimeException | IOException ex) { - // Ignore failure at this point. - Log_OC.w(TAG, "Failed release MediaMetadataRetriever for " + file.getAbsolutePath()); - } } if (thumbnail != null) { @@ -1004,17 +1063,6 @@ public final class ThumbnailsCacheManager { } } - /** - * Converts size of file icon from dp to pixel - * - * @return int - */ - private int getAvatarDimension() { - // Converts dp to pixel - Resources r = MainApp.getAppContext().getResources(); - return Math.round(r.getDimension(R.dimen.file_avatar_size)); - } - private @NonNull Drawable doAvatarInBackground() { Bitmap avatar; @@ -1108,7 +1156,7 @@ public final class ThumbnailsCacheManager { try { return TextDrawable.createAvatarByUserId(displayName, mAvatarRadius); } catch (Exception e1) { - return ResourcesCompat.getDrawable(mResources, R.drawable.ic_user, null); + return ResourcesCompat.getDrawable(mResources, R.drawable.ic_user_outline, null); } } else { return BitmapUtils.bitmapToCircularBitmapDrawable(mResources, avatar); @@ -1138,8 +1186,7 @@ public final class ThumbnailsCacheManager { public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncThumbnailDrawable) { - final AsyncThumbnailDrawable asyncDrawable = (AsyncThumbnailDrawable) drawable; + if (drawable instanceof AsyncThumbnailDrawable asyncDrawable) { return asyncDrawable.getBitmapWorkerTask(); } } @@ -1149,8 +1196,7 @@ public final class ThumbnailsCacheManager { private static ResizedImageGenerationTask getResizedImageGenerationWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncResizedImageDrawable) { - final AsyncResizedImageDrawable asyncDrawable = (AsyncResizedImageDrawable) drawable; + if (drawable instanceof AsyncResizedImageDrawable asyncDrawable) { return asyncDrawable.getBitmapWorkerTask(); } } @@ -1160,8 +1206,7 @@ public final class ThumbnailsCacheManager { private static GalleryImageGenerationTask getGalleryImageGenerationTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncGalleryImageDrawable) { - final AsyncGalleryImageDrawable asyncDrawable = (AsyncGalleryImageDrawable) drawable; + if (drawable instanceof AsyncGalleryImageDrawable asyncDrawable) { return asyncDrawable.getBitmapWorkerTask(); } } @@ -1248,9 +1293,11 @@ public final class ThumbnailsCacheManager { } /** - * adapted from https://stackoverflow.com/a/8113368 + * adapted from ... */ private static Bitmap handlePNG(Bitmap source, int newWidth, int newHeight) { + Bitmap softwareBitmap = source.copy(Bitmap.Config.ARGB_8888, false); + int sourceWidth = source.getWidth(); int sourceHeight = source.getHeight(); @@ -1269,8 +1316,9 @@ public final class ThumbnailsCacheManager { Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(dest); - canvas.drawColor(MainApp.getAppContext().getResources().getColor(R.color.background_color_png)); - canvas.drawBitmap(source, null, targetRect, null); + int color = ContextCompat.getColor(MainApp.getAppContext(),R.color.background_color_png); + canvas.drawColor(color); + canvas.drawBitmap(softwareBitmap, null, targetRect, null); return dest; } @@ -1388,7 +1436,7 @@ public final class ThumbnailsCacheManager { GetMethod getMethod = null; try { String uri = mClient.getBaseUri() + "/index.php/core/preview?fileId=" - + file.getRemoteId() + + file.getLocalId() + "&x=" + (pxW / 2) + "&y=" + (pxH / 2) + "&a=1&mode=cover&forceIcon=0"; Log_OC.d(TAG, "generate resized image: " + file.getFileName() + " URI: " + uri); getMethod = new GetMethod(uri); @@ -1422,9 +1470,10 @@ public final class ThumbnailsCacheManager { } } - // resized dimensions + // resized dimensions and set update thumbnail needed to false to prevent rendering loop if (thumbnail != null) { file.setImageDimension(new ImageDimension(thumbnail.getWidth(), thumbnail.getHeight())); + file.setUpdateThumbnailNeeded(false); storageManager.saveFile(file); } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 51143ec..d7b2982 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -10,7 +10,7 @@ * SPDX-FileCopyrightText: 2016 María Asensio Valverde * SPDX-FileCopyrightText: 2016 David A. Velasco * SPDX-FileCopyrightText: 2014 Luke Owncloud - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datamodel; @@ -25,8 +25,13 @@ import android.os.RemoteException; import com.nextcloud.client.account.CurrentAccountProvider; import com.nextcloud.client.account.User; +import com.nextcloud.client.database.NextcloudDatabase; +import com.nextcloud.client.database.dao.UploadDao; +import com.nextcloud.client.database.entity.UploadEntity; +import com.nextcloud.client.database.entity.UploadEntityKt; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadWorker; +import com.nextcloud.utils.autoRename.AutoRename; import com.owncloud.android.MainApp; import com.owncloud.android.db.OCUpload; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; @@ -34,7 +39,9 @@ import com.owncloud.android.db.UploadResult; import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.operations.UploadFileOperation; +import com.owncloud.android.utils.theme.CapabilityUtils; import java.io.File; import java.util.ArrayList; @@ -54,13 +61,19 @@ import androidx.annotation.VisibleForTesting; public class UploadsStorageManager extends Observable { private static final String TAG = UploadsStorageManager.class.getSimpleName(); + private static final String IS_EQUAL = "== ?"; + private static final String EQUAL = "=="; + private static final String OR = " OR "; private static final String AND = " AND "; + private static final String ANGLE_BRACKETS = "<>"; private static final int SINGLE_RESULT = 1; private static final long QUERY_PAGE_SIZE = 100; private final ContentResolver contentResolver; private final CurrentAccountProvider currentAccountProvider; + private OCCapability capability; + public final UploadDao uploadDao = NextcloudDatabase.getInstance(MainApp.getAppContext()).uploadDao(); public UploadsStorageManager( CurrentAccountProvider currentAccountProvider, @@ -73,6 +86,14 @@ public class UploadsStorageManager extends Observable { this.currentAccountProvider = currentAccountProvider; } + private void initOCCapability() { + try { + this.capability = CapabilityUtils.getCapability(MainApp.getAppContext()); + } catch (RuntimeException e) { + Log_OC.e(TAG,"Failed to set OCCapability: Dependencies are not yet ready."); + } + } + /** * Stores an upload object in DB. * @@ -110,7 +131,7 @@ public class UploadsStorageManager extends Observable { } - public long[] storeUploads(final List ocUploads) { + public void storeUploads(final List ocUploads) { Log_OC.v(TAG, "Inserting " + ocUploads.size() + " uploads"); ArrayList operations = new ArrayList<>(ocUploads.size()); for (OCUpload ocUpload : ocUploads) { @@ -133,20 +154,15 @@ public class UploadsStorageManager extends Observable { try { final ContentProviderResult[] contentProviderResults = getDB().applyBatch(MainApp.getAuthority(), operations); - final long[] newIds = new long[ocUploads.size()]; for (int i = 0; i < contentProviderResults.length; i++) { final ContentProviderResult result = contentProviderResults[i]; final long new_id = Long.parseLong(result.uri.getPathSegments().get(1)); ocUploads.get(i).setUploadId(new_id); - newIds[i] = new_id; } notifyObserversNow(); - return newIds; } catch (OperationApplicationException | RemoteException e) { Log_OC.e(TAG, "Error inserting uploads", e); } - - return null; } @NonNull @@ -175,9 +191,22 @@ public class UploadsStorageManager extends Observable { * @param ocUpload Upload object with state to update * @return num of updated uploads. */ - public int updateUpload(OCUpload ocUpload) { + public synchronized int updateUpload(OCUpload ocUpload) { Log_OC.v(TAG, "Updating " + ocUpload.getLocalPath() + " with status=" + ocUpload.getUploadStatus()); + OCUpload existingUpload = getUploadById(ocUpload.getUploadId()); + if (existingUpload == null) { + Log_OC.e(TAG, "Upload not found for ID: " + ocUpload.getUploadId()); + return 0; + } + + if (!existingUpload.getAccountName().equals(ocUpload.getAccountName())) { + Log_OC.e(TAG, "Account mismatch for upload ID " + ocUpload.getUploadId() + + ": expected " + existingUpload.getAccountName() + + ", got " + ocUpload.getAccountName()); + return 0; + } + ContentValues cv = new ContentValues(); cv.put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.getLocalPath()); cv.put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.getRemotePath()); @@ -190,8 +219,8 @@ public class UploadsStorageManager extends Observable { int result = getDB().update(ProviderTableMeta.CONTENT_URI_UPLOADS, cv, - ProviderTableMeta._ID + "=?", - new String[]{String.valueOf(ocUpload.getUploadId())} + ProviderTableMeta._ID + "=? AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?", + new String[]{String.valueOf(ocUpload.getUploadId()), ocUpload.getAccountName()} ); Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.getLocalPath()); @@ -247,11 +276,10 @@ public class UploadsStorageManager extends Observable { * @param localPath path of the file to upload in the device storage * @return 1 if file status was updated, else 0. */ - private int updateUploadStatus(long id, UploadStatus status, UploadResult result, String remotePath, + private void updateUploadStatus(long id, UploadStatus status, UploadResult result, String remotePath, String localPath) { //Log_OC.v(TAG, "Updating "+filepath+" with uploadStatus="+status +" and result="+result); - int returnValue = 0; Cursor c = getDB().query( ProviderTableMeta.CONTENT_URI_UPLOADS, null, @@ -265,14 +293,13 @@ public class UploadsStorageManager extends Observable { Log_OC.e(TAG, c.getCount() + " items for id=" + id + " available in UploadDb. Expected 1. Failed to update upload db."); } else { - returnValue = updateUploadInternal(c, status, result, remotePath, localPath); + updateUploadInternal(c, status, result, remotePath, localPath); } c.close(); } else { Log_OC.e(TAG, "Cursor is null"); } - return returnValue; } /** @@ -425,6 +452,20 @@ public class UploadsStorageManager extends Observable { return result; } + public List getUploadsByIds(long[] uploadIds, String accountName) { + final List result = new ArrayList<>(); + + final List entities = uploadDao.getUploadsByIds(uploadIds, accountName); + entities.forEach(uploadEntity -> { + OCUpload ocUpload = createOCUploadFromEntity(uploadEntity); + if (ocUpload != null) { + result.add(ocUpload); + } + }); + + return result; + } + private OCUpload[] getUploads(@Nullable String selection, @Nullable String... selectionArgs) { final List uploads = new ArrayList<>(); long page = 0; @@ -464,11 +505,24 @@ public class UploadsStorageManager extends Observable { @NonNull private List getUploadPage(final long afterId, @Nullable String selection, @Nullable String... selectionArgs) { - return getUploadPage(afterId, true, selection, selectionArgs); + return getUploadPage(QUERY_PAGE_SIZE, afterId, true, selection, selectionArgs); + } + + private String getInProgressAndDelayedUploadsSelection() { + return "( " + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_IN_PROGRESS.value + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_WIFI.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.LOCK_FAILED.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_CHARGING.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + " ) AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL; } @NonNull - private List getUploadPage(final long afterId, final boolean descending, @Nullable String selection, @Nullable String... selectionArgs) { + private List getUploadPage(long limit, final long afterId, final boolean descending, @Nullable String selection, @Nullable String... selectionArgs) { List uploads = new ArrayList<>(); String pageSelection = selection; String[] pageSelectionArgs = selectionArgs; @@ -499,13 +553,20 @@ public class UploadsStorageManager extends Observable { } else { Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", selection, afterId)); } + + String sortOrder; + if (limit > 0) { + sortOrder = String.format(Locale.ENGLISH, "_id " + sortDirection + " LIMIT %d", limit); + } else { + sortOrder = String.format(Locale.ENGLISH, "_id " + sortDirection); + } + Cursor c = getDB().query( ProviderTableMeta.CONTENT_URI_UPLOADS, null, pageSelection, pageSelectionArgs, - String.format(Locale.ENGLISH, "_id " + sortDirection + " LIMIT %d", QUERY_PAGE_SIZE) - ); + sortOrder); if (c != null) { if (c.moveToFirst()) { @@ -523,11 +584,27 @@ public class UploadsStorageManager extends Observable { return uploads; } + @Nullable + private OCUpload createOCUploadFromEntity(UploadEntity entity) { + if (entity == null) { + return null; + } + initOCCapability(); + return UploadEntityKt.toOCUpload(entity, capability); + } + private OCUpload createOCUploadFromCursor(Cursor c) { + initOCCapability(); + OCUpload upload = null; if (c != null) { String localPath = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_LOCAL_PATH)); + String remotePath = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_REMOTE_PATH)); + if (capability != null) { + remotePath = AutoRename.INSTANCE.rename(remotePath, capability); + } + String accountName = c.getString(c.getColumnIndexOrThrow(ProviderTableMeta.UPLOADS_ACCOUNT_NAME)); upload = new OCUpload(localPath, remotePath, accountName); @@ -560,150 +637,101 @@ public class UploadsStorageManager extends Observable { } public OCUpload[] getCurrentAndPendingUploadsForAccount(final @NonNull String accountName) { - return getUploads("( " + ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.LOCK_FAILED.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - " ) AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", - accountName); + String inProgressUploadsSelection = getInProgressAndDelayedUploadsSelection(); + return getUploads(inProgressUploadsSelection, accountName); } - /** - * Gets a page of uploads after afterId, where uploads are sorted by ascending upload id. - *

    - * If afterId is -1, returns the first page - */ - public List getCurrentAndPendingUploadsForAccountPageAscById(final long afterId, final @NonNull String accountName) { - final String selection = "( " + ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.LOCK_FAILED.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - " ) AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?"; - return getUploadPage(afterId, false, selection, accountName); + public long[] getCurrentUploadIds(final @NonNull String accountName) { + final var result = uploadDao.getAllIds(UploadStatus.UPLOAD_IN_PROGRESS.value, accountName); + return result.stream() + .mapToLong(Integer::longValue) + .toArray(); } /** * Get all failed uploads. */ public OCUpload[] getFailedUploads() { - return getUploads("(" + ProviderTableMeta.UPLOADS_STATUS + "== ?" + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.LOCK_FAILED.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + return getUploads("(" + ProviderTableMeta.UPLOADS_STATUS + IS_EQUAL + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_WIFI.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.LOCK_FAILED.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_CHARGING.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + " ) AND " + ProviderTableMeta.UPLOADS_LAST_RESULT + "!= " + UploadResult.VIRUS_DETECTED.getValue() , String.valueOf(UploadStatus.UPLOAD_FAILED.value)); } public OCUpload[] getUploadsForAccount(final @NonNull String accountName) { - return getUploads(ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", accountName); + return getUploads(ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, accountName); } public OCUpload[] getFinishedUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", user.getAccountName()); + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_SUCCEEDED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, user.getAccountName()); } public OCUpload[] getCancelledUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_CANCELLED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", user.getAccountName()); - } - - /** - * Get all uploads which where successfully completed. - */ - public OCUpload[] getFinishedUploads() { - - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value, (String[]) null); + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_CANCELLED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, user.getAccountName()); } public OCUpload[] getFailedButNotDelayedUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_FAILED.value + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.LOCK_FAILED.getValue() + + ANGLE_BRACKETS + UploadResult.LOCK_FAILED.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", + ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, user.getAccountName()); } - /** - * Get all failed uploads, except for those that were not performed due to lack of Wifi connection. - * - * @return Array of failed uploads, except for those that were not performed due to lack of Wifi connection. - */ - public OCUpload[] getFailedButNotDelayedUploads() { - - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue(), - (String[]) null - ); - } - private ContentResolver getDB() { return contentResolver; } - public long clearFailedButNotDelayedUploads() { + public void clearFailedButNotDelayedUploads() { User user = currentAccountProvider.getUser(); final long deleted = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_FAILED.value + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.LOCK_FAILED.getValue() + + ANGLE_BRACKETS + UploadResult.LOCK_FAILED.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", + ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} ); Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi"); if (deleted > 0) { notifyObserversNow(); } - return deleted; } public void clearCancelledUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); final long deleted = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_CANCELLED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{user.getAccountName()} + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_CANCELLED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} ); Log_OC.d(TAG, "delete all cancelled uploads"); @@ -712,19 +740,18 @@ public class UploadsStorageManager extends Observable { } } - public long clearSuccessfulUploads() { + public void clearSuccessfulUploads() { User user = currentAccountProvider.getUser(); final long deleted = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{user.getAccountName()} + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_SUCCEEDED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} ); Log_OC.d(TAG, "delete all successful uploads"); if (deleted > 0) { notifyObserversNow(); } - return deleted; } /** @@ -798,43 +825,10 @@ public class UploadsStorageManager extends Observable { ); } - /** - * Changes the status of any in progress upload from UploadStatus.UPLOAD_IN_PROGRESS to UploadStatus.UPLOAD_FAILED - * - * @return Number of uploads which status was changed. - */ - public int failInProgressUploads(UploadResult fail) { - Log_OC.v(TAG, "Updating state of any killed upload"); - - ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.UPLOADS_STATUS, UploadStatus.UPLOAD_FAILED.getValue()); - cv.put( - ProviderTableMeta.UPLOADS_LAST_RESULT, - fail != null ? fail.getValue() : UploadResult.UNKNOWN.getValue() - ); - cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, Calendar.getInstance().getTimeInMillis()); - - int result = getDB().update( - ProviderTableMeta.CONTENT_URI_UPLOADS, - cv, - ProviderTableMeta.UPLOADS_STATUS + "=?", - new String[]{String.valueOf(UploadStatus.UPLOAD_IN_PROGRESS.getValue())} - ); - - if (result == 0) { - Log_OC.v(TAG, "No upload was killed"); - } else { - Log_OC.w(TAG, Integer.toString(result) + " uploads where abruptly interrupted"); - notifyObserversNow(); - } - - return result; - } - @VisibleForTesting - public int removeAllUploads() { + public void removeAllUploads() { Log_OC.v(TAG, "Delete all uploads!"); - return getDB().delete( + getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, "", new String[]{}); @@ -877,17 +871,13 @@ public class UploadsStorageManager extends Observable { } public static UploadStatus fromValue(int value) { - switch (value) { - case 0: - return UPLOAD_IN_PROGRESS; - case 1: - return UPLOAD_FAILED; - case 2: - return UPLOAD_SUCCEEDED; - case 3: - return UPLOAD_CANCELLED; - } - return null; + return switch (value) { + case 0 -> UPLOAD_IN_PROGRESS; + case 1 -> UPLOAD_FAILED; + case 2 -> UPLOAD_SUCCEEDED; + case 3 -> UPLOAD_CANCELLED; + default -> null; + }; } public int getValue() { diff --git a/app/src/main/java/com/owncloud/android/datamodel/VirtualFolderType.java b/app/src/main/java/com/owncloud/android/datamodel/VirtualFolderType.java index a867615..1259b70 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/VirtualFolderType.java +++ b/app/src/main/java/com/owncloud/android/datamodel/VirtualFolderType.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Data.java b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Data.java index 422f2d7..da3ad49 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Data.java +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Data.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.decrypted; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFile.java b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFile.java index a2e8660..dcd2d78 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFile.java +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFile.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.decrypted; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFolderMetadataFileV1.java b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFolderMetadataFileV1.java index d362022..c80021f 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFolderMetadataFileV1.java +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedFolderMetadataFileV1.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.decrypted; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedMetadata.java b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedMetadata.java index 8b65475..2d2036e 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedMetadata.java +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/DecryptedMetadata.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.decrypted; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Encrypted.java b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Encrypted.java index 5a5e4fa..3d566c0 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Encrypted.java +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Encrypted.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.decrypted; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Sharing.java b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Sharing.java index 94d2d3b..6bb5b89 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Sharing.java +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/decrypted/Sharing.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.decrypted; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFile.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFile.kt index 7e7905a..63a9573 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFile.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFile.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.encrypted diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFolderMetadataFileV1.java b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFolderMetadataFileV1.java index eb814d4..17d8eae 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFolderMetadataFileV1.java +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v1/encrypted/EncryptedFolderMetadataFileV1.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v1.encrypted; diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFile.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFile.kt index c7d619a..406f859 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFile.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFile.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.decrypted diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFolderMetadataFile.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFolderMetadataFile.kt index 6c6b1e3..c092b8d 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFolderMetadataFile.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedFolderMetadataFile.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.decrypted diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt index 88099b6..7401571 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedMetadata.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.decrypted diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedUser.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedUser.kt index 31352d6..ac02b66 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedUser.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/decrypted/DecryptedUser.kt @@ -3,11 +3,8 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.decrypted -data class DecryptedUser( - val userId: String, - val certificate: String -) +data class DecryptedUser(val userId: String, val certificate: String, var decryptedMetadataKey: String?) diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledrop.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledrop.kt index 801e04a..3f36adf 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledrop.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledrop.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.encrypted diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledropUser.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledropUser.kt index a8c0998..bbe3e1c 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledropUser.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFiledropUser.kt @@ -3,11 +3,8 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.encrypted -data class EncryptedFiledropUser( - val userId: String, - val encryptedFiledropKey: String -) +data class EncryptedFiledropUser(val userId: String, val encryptedFiledropKey: String) diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt index 1a1644a..5c7b279 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedFolderMetadataFile.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.encrypted diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedMetadata.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedMetadata.kt index 981c666..c060aa7 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedMetadata.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedMetadata.kt @@ -3,12 +3,8 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.encrypted -data class EncryptedMetadata( - val ciphertext: String, - val nonce: String, - val authenticationTag: String -) +data class EncryptedMetadata(val ciphertext: String, val nonce: String, val authenticationTag: String) diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedUser.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedUser.kt index 757353d..97a2c0a 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedUser.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/EncryptedUser.kt @@ -3,12 +3,8 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.encrypted -data class EncryptedUser( - val userId: String, - val certificate: String, - val encryptedMetadataKey: String -) +data class EncryptedUser(val userId: String, val certificate: String, val encryptedMetadataKey: String) diff --git a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/FiledropData.kt b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/FiledropData.kt index 6c92b42..bbefb6f 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/FiledropData.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/e2e/v2/encrypted/FiledropData.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel.e2e.v2.encrypted diff --git a/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermission.kt b/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermission.kt new file mode 100644 index 0000000..460edeb --- /dev/null +++ b/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermission.kt @@ -0,0 +1,10 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.datamodel.quickPermission + +data class QuickPermission(val type: QuickPermissionType, var isSelected: Boolean) diff --git a/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt b/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt new file mode 100644 index 0000000..a09aa06 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt @@ -0,0 +1,51 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.datamodel.quickPermission + +import android.content.Context +import android.graphics.drawable.Drawable +import androidx.core.content.ContextCompat +import com.owncloud.android.R +import com.owncloud.android.lib.resources.shares.OCShare + +enum class QuickPermissionType(val iconId: Int, val textId: Int) { + NONE(R.drawable.ic_unknown, R.string.unknown), + VIEW_ONLY(R.drawable.ic_eye, R.string.share_permission_view_only), + CAN_EDIT(R.drawable.ic_edit, R.string.share_permission_can_edit), + FILE_REQUEST(R.drawable.ic_file_request, R.string.share_permission_file_request), + SECURE_FILE_DROP(R.drawable.ic_file_request, R.string.share_permission_secure_file_drop), + CUSTOM_PERMISSIONS(R.drawable.ic_custom_permissions, R.string.share_custom_permission); + + fun getText(context: Context): String = context.getString(textId) + + fun getIcon(context: Context): Drawable? = ContextCompat.getDrawable(context, iconId) + + fun getPermissionFlag(isFolder: Boolean): Int = when (this) { + NONE -> OCShare.NO_PERMISSION + VIEW_ONLY -> OCShare.READ_PERMISSION_FLAG + CAN_EDIT -> if (isFolder) OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER else OCShare.MAXIMUM_PERMISSIONS_FOR_FILE + FILE_REQUEST -> OCShare.CREATE_PERMISSION_FLAG + SECURE_FILE_DROP -> OCShare.CREATE_PERMISSION_FLAG + OCShare.READ_PERMISSION_FLAG + else -> { + // Custom permission's flag can't be determined + OCShare.NO_PERMISSION + } + } + + fun getAvailablePermissions(hasFileRequestPermission: Boolean): List { + val permissions = listOf(VIEW_ONLY, CAN_EDIT, FILE_REQUEST, CUSTOM_PERMISSIONS) + val result = if (hasFileRequestPermission) permissions else permissions.filter { it != FILE_REQUEST } + + return result.map { type -> + QuickPermission( + type = type, + isSelected = (type == this) + ) + } + } +} diff --git a/app/src/main/java/com/owncloud/android/datastorage/DataStorageProvider.java b/app/src/main/java/com/owncloud/android/datastorage/DataStorageProvider.java index cada61b..deb7568 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/DataStorageProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/DataStorageProvider.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datastorage; diff --git a/app/src/main/java/com/owncloud/android/datastorage/StoragePoint.java b/app/src/main/java/com/owncloud/android/datastorage/StoragePoint.java index 4516e53..8fa7d83 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/StoragePoint.java +++ b/app/src/main/java/com/owncloud/android/datastorage/StoragePoint.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage; diff --git a/app/src/main/java/com/owncloud/android/datastorage/UniqueStorageList.java b/app/src/main/java/com/owncloud/android/datastorage/UniqueStorageList.java index 2319490..c421e55 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/UniqueStorageList.java +++ b/app/src/main/java/com/owncloud/android/datastorage/UniqueStorageList.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage; diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractCommandLineStoragePoint.java b/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractCommandLineStoragePoint.java index 4e0d494..66dcb12 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractCommandLineStoragePoint.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractCommandLineStoragePoint.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; @@ -44,7 +44,7 @@ abstract class AbstractCommandLineStoragePoint extends AbstractStoragePointProvi process.waitFor(); final InputStream is = process.getInputStream(); - final byte buffer[] = new byte[1024]; + final byte[] buffer = new byte[1024]; while (is.read(buffer) != -1) { s.append(new String(buffer, "UTF8")); } diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractStoragePointProvider.java b/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractStoragePointProvider.java index cce5b45..bc9b9ff 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractStoragePointProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/AbstractStoragePointProvider.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/EnvironmentStoragePointProvider.java b/app/src/main/java/com/owncloud/android/datastorage/providers/EnvironmentStoragePointProvider.java index 3842bb6..868dee9 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/EnvironmentStoragePointProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/EnvironmentStoragePointProvider.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/HardcodedStoragePointProvider.java b/app/src/main/java/com/owncloud/android/datastorage/providers/HardcodedStoragePointProvider.java index d847757..dcd6026 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/HardcodedStoragePointProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/HardcodedStoragePointProvider.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/IStoragePointProvider.java b/app/src/main/java/com/owncloud/android/datastorage/providers/IStoragePointProvider.java index ed261c1..5f8f38b 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/IStoragePointProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/IStoragePointProvider.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2016-2018 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/MountCommandStoragePointProvider.java b/app/src/main/java/com/owncloud/android/datastorage/providers/MountCommandStoragePointProvider.java index 0b28da4..2cfc52c 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/MountCommandStoragePointProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/MountCommandStoragePointProvider.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; @@ -49,7 +49,7 @@ public class MountCommandStoragePointProvider extends AbstractCommandLineStorage for (String line : mounted.split("\n")) { if (!line.toLowerCase(Locale.US).contains("asec") && sPattern.matcher(line).matches()) { - String parts[] = line.split(" "); + String[] parts = line.split(" "); for (String path : parts) { if (path.length() > 0 && path.charAt(0) == '/' && !path.toLowerCase(Locale.US).contains("vold")) { result.add(path); diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/SystemDefaultStoragePointProvider.java b/app/src/main/java/com/owncloud/android/datastorage/providers/SystemDefaultStoragePointProvider.java index 7914914..7706a77 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/SystemDefaultStoragePointProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/SystemDefaultStoragePointProvider.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; diff --git a/app/src/main/java/com/owncloud/android/datastorage/providers/VDCStoragePointProvider.java b/app/src/main/java/com/owncloud/android/datastorage/providers/VDCStoragePointProvider.java index b497862..f2d6959 100644 --- a/app/src/main/java/com/owncloud/android/datastorage/providers/VDCStoragePointProvider.java +++ b/app/src/main/java/com/owncloud/android/datastorage/providers/VDCStoragePointProvider.java @@ -5,7 +5,7 @@ * Copyright (C) 2016 Nextcloud * Copyright (C) 2016 Bartosz Przybylski * - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.datastorage.providers; @@ -28,11 +28,8 @@ public class VDCStoragePointProvider extends AbstractCommandLineStoragePoint { @Override public List getAvailableStoragePoint() { - List result = new Vector<>(); - result.addAll(getPaths(getCommandLineResult())); - - return result; + return new Vector<>(getPaths(getCommandLineResult())); } @Override @@ -44,7 +41,7 @@ public class VDCStoragePointProvider extends AbstractCommandLineStoragePoint { List result = new Vector<>(); for (String line : vdcResources.split("\n")) { - String vdcLine[] = line.split(" "); + String[] vdcLine = line.split(" "); try { int status = Integer.parseInt(vdcLine[0]); if (status != sVDCVolumeList) { diff --git a/app/src/main/java/com/owncloud/android/db/OCUpload.java b/app/src/main/java/com/owncloud/android/db/OCUpload.java index 2cf40cf..167e943 100644 --- a/app/src/main/java/com/owncloud/android/db/OCUpload.java +++ b/app/src/main/java/com/owncloud/android/db/OCUpload.java @@ -10,7 +10,7 @@ * SPDX-FileCopyrightText: 2014 Luke Owncloud * SPDX-FileCopyrightText: 2015-2016 David A. Velasco * SPDX-FileCopyrightText: 2015-2016 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.db; @@ -238,7 +238,7 @@ public class OCUpload implements Parcelable { /**** * */ - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<>() { @Override public OCUpload createFromParcel(Parcel source) { @@ -292,10 +292,9 @@ public class OCUpload implements Parcelable { @SuppressFBWarnings("SEO_SUBOPTIMAL_EXPRESSION_ORDER") public boolean isSame(@Nullable Object obj) { - if (!(obj instanceof OCUpload)) { + if (!(obj instanceof OCUpload other)) { return false; } - OCUpload other = (OCUpload) obj; return this.uploadId == other.uploadId && localPath.equals(other.localPath) && remotePath.equals(other.remotePath) && diff --git a/app/src/main/java/com/owncloud/android/db/OCUploadComparator.kt b/app/src/main/java/com/owncloud/android/db/OCUploadComparator.kt index 320de12..5c73c9f 100644 --- a/app/src/main/java/com/owncloud/android/db/OCUploadComparator.kt +++ b/app/src/main/java/com/owncloud/android/db/OCUploadComparator.kt @@ -45,19 +45,15 @@ class OCUploadComparator : Comparator { return 0 } - private fun compareUploadStatus(upload1: OCUpload, upload2: OCUpload): Int { - return upload1.fixedUploadStatus.compareTo(upload2.fixedUploadStatus) - } + private fun compareUploadStatus(upload1: OCUpload, upload2: OCUpload): Int = + upload1.fixedUploadStatus.compareTo(upload2.fixedUploadStatus) - private fun compareUploadingNow(upload1: OCUpload, upload2: OCUpload): Int { - return upload2.isFixedUploadingNow.compareTo(upload1.isFixedUploadingNow) - } + private fun compareUploadingNow(upload1: OCUpload, upload2: OCUpload): Int = + upload2.isFixedUploadingNow.compareTo(upload1.isFixedUploadingNow) - private fun compareUpdateTime(upload1: OCUpload, upload2: OCUpload): Int { - return upload2.fixedUploadEndTimeStamp.compareTo(upload1.fixedUploadEndTimeStamp) - } + private fun compareUpdateTime(upload1: OCUpload, upload2: OCUpload): Int = + upload2.fixedUploadEndTimeStamp.compareTo(upload1.fixedUploadEndTimeStamp) - private fun compareUploadId(upload1: OCUpload, upload2: OCUpload): Int { - return upload1.fixedUploadId.compareTo(upload2.fixedUploadId) - } + private fun compareUploadId(upload1: OCUpload, upload2: OCUpload): Int = + upload1.fixedUploadId.compareTo(upload2.fixedUploadId) } diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index a22d5d8..bd57f27 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2014-2015 María Asensio Valverde * SPDX-FileCopyrightText: 2012 David A. Velasco * SPDX-FileCopyrightText: 2011 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.db; @@ -16,8 +16,6 @@ import android.provider.BaseColumns; import com.owncloud.android.MainApp; -import java.util.Arrays; -import java.util.Collections; import java.util.List; /** @@ -25,13 +23,27 @@ import java.util.List; */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 81; + public static final int DB_VERSION = 95; private ProviderMeta() { // No instance } static public class ProviderTableMeta implements BaseColumns { + // region Recommended files table + public static final String RECOMMENDED_FILE_TABLE_NAME = "recommended_files"; + public static final String RECOMMENDED_FILE_NAME = "name"; + public static final String RECOMMENDED_FILE_ACCOUNT_NAME = "account_name"; + public static final String RECOMMENDED_FILE_DIRECTORY = "directory"; + public static final String RECOMMENDED_FILE_EXTENSIONS = "extension"; + public static final String RECOMMENDED_FILE_MIME_TYPE = "mime_type"; + public static final String RECOMMENDED_FILE_HAS_PREVIEW = "has_preview"; + public static final String RECOMMENDED_FILE_REASON = "reason"; + public static final String RECOMMENDED_TIMESTAMP = "timestamp"; + // endregion + + // region Table names + public static final String OFFLINE_OPERATION_TABLE_NAME = "offline_operations"; public static final String FILE_TABLE_NAME = "filelist"; public static final String OCSHARES_TABLE_NAME = "ocshares"; public static final String CAPABILITIES_TABLE_NAME = "capabilities"; @@ -43,28 +55,29 @@ public class ProviderMeta { public static final String FILESYSTEM_TABLE_NAME = "filesystem"; public static final String EDITORS_TABLE_NAME = "editors"; public static final String CREATORS_TABLE_NAME = "creators"; + // endregion private static final String CONTENT_PREFIX = "content://"; public static final Uri CONTENT_URI = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/"); + + MainApp.getAuthority() + "/"); public static final Uri CONTENT_URI_FILE = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/file"); + + MainApp.getAuthority() + "/file"); public static final Uri CONTENT_URI_DIR = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/dir"); + + MainApp.getAuthority() + "/dir"); public static final Uri CONTENT_URI_SHARE = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/shares"); + + MainApp.getAuthority() + "/shares"); public static final Uri CONTENT_URI_CAPABILITIES = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/capabilities"); + + MainApp.getAuthority() + "/capabilities"); public static final Uri CONTENT_URI_UPLOADS = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/uploads"); + + MainApp.getAuthority() + "/uploads"); public static final Uri CONTENT_URI_SYNCED_FOLDERS = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/synced_folders"); + + MainApp.getAuthority() + "/synced_folders"); public static final Uri CONTENT_URI_EXTERNAL_LINKS = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/external_links"); + + MainApp.getAuthority() + "/external_links"); public static final Uri CONTENT_URI_VIRTUAL = Uri.parse(CONTENT_PREFIX + MainApp.getAuthority() + "/virtual"); public static final Uri CONTENT_URI_FILESYSTEM = Uri.parse(CONTENT_PREFIX - + MainApp.getAuthority() + "/filesystem"); + + MainApp.getAuthority() + "/filesystem"); public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file"; @@ -76,6 +89,7 @@ public class ProviderMeta { public static final String FILE_ENCRYPTED_NAME = "encrypted_filename"; public static final String FILE_CREATION = "created"; public static final String FILE_MODIFIED = "modified"; + public static final String FILE_UPLOADED = "uploaded"; public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data"; public static final String FILE_CONTENT_LENGTH = "content_length"; public static final String FILE_CONTENT_TYPE = "content_type"; @@ -120,58 +134,62 @@ public class ProviderMeta { public static final String FILE_LOCK_TOKEN = "lock_token"; public static final String FILE_TAGS = "tags"; public static final String FILE_E2E_COUNTER = "e2e_counter"; + public static final String FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP = "internal_two_way_sync_timestamp"; + public static final String FILE_INTERNAL_TWO_WAY_SYNC_RESULT = "internal_two_way_sync_result"; - public static final List FILE_ALL_COLUMNS = Collections.unmodifiableList(Arrays.asList( - _ID, - FILE_PARENT, - FILE_NAME, - FILE_ENCRYPTED_NAME, - FILE_CREATION, - FILE_MODIFIED, - FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, - FILE_CONTENT_LENGTH, - FILE_CONTENT_TYPE, - FILE_STORAGE_PATH, - FILE_PATH, - FILE_PATH_DECRYPTED, - FILE_ACCOUNT_OWNER, - FILE_LAST_SYNC_DATE, - FILE_LAST_SYNC_DATE_FOR_DATA, - FILE_KEEP_IN_SYNC, - FILE_ETAG, - FILE_ETAG_ON_SERVER, - FILE_SHARED_VIA_LINK, - FILE_SHARED_WITH_SHAREE, - FILE_PERMISSIONS, - FILE_REMOTE_ID, - FILE_LOCAL_ID, - FILE_UPDATE_THUMBNAIL, - FILE_IS_DOWNLOADING, - FILE_ETAG_IN_CONFLICT, - FILE_FAVORITE, - FILE_HIDDEN, - FILE_IS_ENCRYPTED, - FILE_MOUNT_TYPE, - FILE_HAS_PREVIEW, - FILE_UNREAD_COMMENTS_COUNT, - FILE_OWNER_ID, - FILE_OWNER_DISPLAY_NAME, - FILE_NOTE, - FILE_SHAREES, - FILE_RICH_WORKSPACE, - FILE_LOCKED, - FILE_LOCK_TYPE, - FILE_LOCK_OWNER, - FILE_LOCK_OWNER_DISPLAY_NAME, - FILE_LOCK_OWNER_EDITOR, - FILE_LOCK_TIMESTAMP, - FILE_LOCK_TIMEOUT, - FILE_LOCK_TOKEN, - FILE_METADATA_SIZE, - FILE_METADATA_LIVE_PHOTO, - FILE_E2E_COUNTER, - FILE_TAGS, - FILE_METADATA_GPS)); + public static final List FILE_ALL_COLUMNS = List.of(_ID, + FILE_PARENT, + FILE_NAME, + FILE_ENCRYPTED_NAME, + FILE_UPLOADED, + FILE_CREATION, + FILE_MODIFIED, + FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, + FILE_CONTENT_LENGTH, + FILE_CONTENT_TYPE, + FILE_STORAGE_PATH, + FILE_PATH, + FILE_PATH_DECRYPTED, + FILE_ACCOUNT_OWNER, + FILE_LAST_SYNC_DATE, + FILE_LAST_SYNC_DATE_FOR_DATA, + FILE_KEEP_IN_SYNC, + FILE_ETAG, + FILE_ETAG_ON_SERVER, + FILE_SHARED_VIA_LINK, + FILE_SHARED_WITH_SHAREE, + FILE_PERMISSIONS, + FILE_REMOTE_ID, + FILE_LOCAL_ID, + FILE_UPDATE_THUMBNAIL, + FILE_IS_DOWNLOADING, + FILE_ETAG_IN_CONFLICT, + FILE_FAVORITE, + FILE_HIDDEN, + FILE_IS_ENCRYPTED, + FILE_MOUNT_TYPE, + FILE_HAS_PREVIEW, + FILE_UNREAD_COMMENTS_COUNT, + FILE_OWNER_ID, + FILE_OWNER_DISPLAY_NAME, + FILE_NOTE, + FILE_SHAREES, + FILE_RICH_WORKSPACE, + FILE_LOCKED, + FILE_LOCK_TYPE, + FILE_LOCK_OWNER, + FILE_LOCK_OWNER_DISPLAY_NAME, + FILE_LOCK_OWNER_EDITOR, + FILE_LOCK_TIMESTAMP, + FILE_LOCK_TIMEOUT, + FILE_LOCK_TOKEN, + FILE_METADATA_SIZE, + FILE_METADATA_LIVE_PHOTO, + FILE_E2E_COUNTER, + FILE_TAGS, + FILE_METADATA_GPS, + FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP, + FILE_INTERNAL_TWO_WAY_SYNC_RESULT); public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc"; // Columns of ocshares table @@ -194,9 +212,12 @@ public class ProviderMeta { public static final String OCSHARES_HIDE_DOWNLOAD = "hide_download"; public static final String OCSHARES_SHARE_LINK = "share_link"; public static final String OCSHARES_SHARE_LABEL = "share_label"; + public static final String OCSHARES_DOWNLOADLIMIT_LIMIT = "download_limit_limit"; + public static final String OCSHARES_DOWNLOADLIMIT_COUNT = "download_limit_count"; + public static final String OCSHARES_ATTRIBUTES = "attributes"; public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE - + " collate nocase asc"; + + " collate nocase asc"; // Columns of capabilities table public static final String CAPABILITIES_ACCOUNT_NAME = "account"; @@ -213,11 +234,11 @@ public class ProviderMeta { public static final String CAPABILITIES_SHARING_PUBLIC_ASK_FOR_OPTIONAL_PASSWORD = "sharing_public_ask_for_optional_password"; public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENABLED = - "sharing_public_expire_date_enabled"; + "sharing_public_expire_date_enabled"; public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_DAYS = - "sharing_public_expire_date_days"; + "sharing_public_expire_date_days"; public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENFORCED = - "sharing_public_expire_date_enforced"; + "sharing_public_expire_date_enforced"; public static final String CAPABILITIES_SHARING_PUBLIC_SEND_MAIL = "sharing_public_send_mail"; public static final String CAPABILITIES_SHARING_PUBLIC_UPLOAD = "sharing_public_upload"; public static final String CAPABILITIES_SHARING_USER_SEND_MAIL = "sharing_user_send_mail"; @@ -255,10 +276,21 @@ public class ProviderMeta { public static final String CAPABILITIES_ETAG = "etag"; public static final String CAPABILITIES_USER_STATUS = "user_status"; public static final String CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI = "user_status_supports_emoji"; + public static final String CAPABILITIES_USER_STATUS_SUPPORTS_BUSY = "user_status_supports_busy"; public static final String CAPABILITIES_ASSISTANT = "assistant"; public static final String CAPABILITIES_GROUPFOLDERS = "groupfolders"; public static final String CAPABILITIES_DROP_ACCOUNT = "drop_account"; public static final String CAPABILITIES_SECURITY_GUARD = "security_guard"; + public static final String CAPABILITIES_FORBIDDEN_FILENAME_CHARACTERS = "forbidden_filename_characters"; + public static final String CAPABILITIES_FORBIDDEN_FILENAMES = "forbidden_filenames"; + public static final String CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS = "forbidden_filename_extensions"; + public static final String CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES = "forbidden_filename_basenames"; + public static final String CAPABILITIES_WINDOWS_COMPATIBLE_FILENAMES = "windows_compatible_filenames"; + public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT = "files_download_limit"; + public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT = "files_download_limit_default"; + public static final String CAPABILITIES_NOTES_FOLDER_PATH = "notes_folder_path"; + public static final String CAPABILITIES_DEFAULT_PERMISSIONS = "default_permissions"; + public static final String CAPABILITIES_HAS_VALID_SUBSCRIPTION = "has_valid_subscription"; //Columns of Uploads table public static final String UPLOADS_LOCAL_PATH = "local_path"; @@ -278,6 +310,15 @@ public class ProviderMeta { public static final String UPLOADS_IS_WIFI_ONLY = "is_wifi_only"; public static final String UPLOADS_FOLDER_UNLOCK_TOKEN = "folder_unlock_token"; + // Columns of offline operation table + public static final String OFFLINE_OPERATION_PARENT_OC_FILE_ID = "offline_operations_parent_oc_file_id"; + public static final String OFFLINE_OPERATION_TYPE = "offline_operations_type"; + public static final String OFFLINE_OPERATION_PATH = "offline_operations_path"; + public static final String OFFLINE_OPERATION_MODIFIED_AT = "offline_operations_modified_at"; + public static final String OFFLINE_OPERATION_CREATED_AT = "offline_operations_created_at"; + public static final String OFFLINE_OPERATION_FILE_NAME = "offline_operations_file_name"; + + // Columns of synced folder table public static final String SYNCED_FOLDER_LOCAL_PATH = "local_path"; public static final String SYNCED_FOLDER_REMOTE_PATH = "remote_path"; @@ -323,6 +364,8 @@ public class ProviderMeta { public static final String FILESYSTEM_SYNCED_FOLDER_ID = "syncedfolder_id"; public static final String FILESYSTEM_CRC32 = "crc32"; + public static final String CAPABILITIES_RECOMMENDATION = "recommendation"; + private ProviderTableMeta() { // No instance } diff --git a/app/src/main/java/com/owncloud/android/db/UploadResult.java b/app/src/main/java/com/owncloud/android/db/UploadResult.java index 4a38dae..8c52300 100644 --- a/app/src/main/java/com/owncloud/android/db/UploadResult.java +++ b/app/src/main/java/com/owncloud/android/db/UploadResult.java @@ -6,12 +6,14 @@ * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2016 David A. Velasco * SPDX-FileCopyrightText: 2015-2016 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.db; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import java.util.Map; + public enum UploadResult { UNKNOWN(-1), UPLOADED(0), @@ -49,120 +51,67 @@ public enum UploadResult { return value; } + private static final Map valueMap = Map.ofEntries( + Map.entry(0, UPLOADED), + Map.entry(1, NETWORK_CONNECTION), + Map.entry(2, CREDENTIAL_ERROR), + Map.entry(3, FOLDER_ERROR), + Map.entry(4, CONFLICT_ERROR), + Map.entry(5, FILE_ERROR), + Map.entry(6, PRIVILEGES_ERROR), + Map.entry(7, CANCELLED), + Map.entry(8, FILE_NOT_FOUND), + Map.entry(9, DELAYED_FOR_WIFI), + Map.entry(10, SERVICE_INTERRUPTED), + Map.entry(11, DELAYED_FOR_CHARGING), + Map.entry(12, MAINTENANCE_MODE), + Map.entry(13, LOCK_FAILED), + Map.entry(14, DELAYED_IN_POWER_SAVE_MODE), + Map.entry(15, SSL_RECOVERABLE_PEER_UNVERIFIED), + Map.entry(16, VIRUS_DETECTED), + Map.entry(17, LOCAL_STORAGE_FULL), + Map.entry(18, OLD_ANDROID_API), + Map.entry(19, SYNC_CONFLICT), + Map.entry(20, CANNOT_CREATE_FILE), + Map.entry(21, LOCAL_STORAGE_NOT_COPIED), + Map.entry(22, QUOTA_EXCEEDED), + Map.entry(23, SAME_FILE_CONFLICT) + ); public static UploadResult fromValue(int value) { - switch (value) { - case -1: - return UNKNOWN; - case 0: - return UPLOADED; - case 1: - return NETWORK_CONNECTION; - case 2: - return CREDENTIAL_ERROR; - case 3: - return FOLDER_ERROR; - case 4: - return CONFLICT_ERROR; - case 5: - return FILE_ERROR; - case 6: - return PRIVILEGES_ERROR; - case 7: - return CANCELLED; - case 8: - return FILE_NOT_FOUND; - case 9: - return DELAYED_FOR_WIFI; - case 10: - return SERVICE_INTERRUPTED; - case 11: - return DELAYED_FOR_CHARGING; - case 12: - return MAINTENANCE_MODE; - case 13: - return LOCK_FAILED; - case 14: - return DELAYED_IN_POWER_SAVE_MODE; - case 15: - return SSL_RECOVERABLE_PEER_UNVERIFIED; - case 16: - return VIRUS_DETECTED; - case 17: - return LOCAL_STORAGE_FULL; - case 18: - return OLD_ANDROID_API; - case 19: - return SYNC_CONFLICT; - case 20: - return CANNOT_CREATE_FILE; - case 21: - return LOCAL_STORAGE_NOT_COPIED; - case 22: - return QUOTA_EXCEEDED; - case 23: - return SAME_FILE_CONFLICT; - } - return UNKNOWN; + return valueMap.getOrDefault(value, UNKNOWN); } public static UploadResult fromOperationResult(RemoteOperationResult result) { - // messy :( - switch (result.getCode()) { - case OK: - return UPLOADED; - case NO_NETWORK_CONNECTION: - case HOST_NOT_AVAILABLE: - case TIMEOUT: - case WRONG_CONNECTION: - case INCORRECT_ADDRESS: - case SSL_ERROR: - return NETWORK_CONNECTION; - case ACCOUNT_EXCEPTION: - case UNAUTHORIZED: - return CREDENTIAL_ERROR; - case FILE_NOT_FOUND: - return FOLDER_ERROR; - case LOCAL_FILE_NOT_FOUND: - return FILE_NOT_FOUND; - case CONFLICT: - return CONFLICT_ERROR; - case LOCAL_STORAGE_NOT_COPIED: - return LOCAL_STORAGE_NOT_COPIED; - case LOCAL_STORAGE_FULL: - return LOCAL_STORAGE_FULL; - case OLD_ANDROID_API: - return OLD_ANDROID_API; - case SYNC_CONFLICT: - return SYNC_CONFLICT; - case FORBIDDEN: - return PRIVILEGES_ERROR; - case CANCELLED: - return CANCELLED; - case DELAYED_FOR_WIFI: - return DELAYED_FOR_WIFI; - case DELAYED_FOR_CHARGING: - return DELAYED_FOR_CHARGING; - case DELAYED_IN_POWER_SAVE_MODE: - return DELAYED_IN_POWER_SAVE_MODE; - case MAINTENANCE_MODE: - return MAINTENANCE_MODE; - case SSL_RECOVERABLE_PEER_UNVERIFIED: - return SSL_RECOVERABLE_PEER_UNVERIFIED; - case UNKNOWN_ERROR: + return switch (result.getCode()) { + case OK -> UPLOADED; + case NO_NETWORK_CONNECTION, HOST_NOT_AVAILABLE, TIMEOUT, WRONG_CONNECTION, INCORRECT_ADDRESS, SSL_ERROR -> + NETWORK_CONNECTION; + case ACCOUNT_EXCEPTION, UNAUTHORIZED -> CREDENTIAL_ERROR; + case FILE_NOT_FOUND -> FOLDER_ERROR; + case LOCAL_FILE_NOT_FOUND -> FILE_NOT_FOUND; + case CONFLICT -> CONFLICT_ERROR; + case LOCAL_STORAGE_NOT_COPIED -> LOCAL_STORAGE_NOT_COPIED; + case LOCAL_STORAGE_FULL -> LOCAL_STORAGE_FULL; + case OLD_ANDROID_API -> OLD_ANDROID_API; + case SYNC_CONFLICT -> SYNC_CONFLICT; + case FORBIDDEN -> PRIVILEGES_ERROR; + case CANCELLED -> CANCELLED; + case DELAYED_FOR_WIFI -> DELAYED_FOR_WIFI; + case DELAYED_FOR_CHARGING -> DELAYED_FOR_CHARGING; + case DELAYED_IN_POWER_SAVE_MODE -> DELAYED_IN_POWER_SAVE_MODE; + case MAINTENANCE_MODE -> MAINTENANCE_MODE; + case SSL_RECOVERABLE_PEER_UNVERIFIED -> SSL_RECOVERABLE_PEER_UNVERIFIED; + case UNKNOWN_ERROR -> { if (result.getException() instanceof java.io.FileNotFoundException) { - return FILE_ERROR; + yield FILE_ERROR; } - return UNKNOWN; - case LOCK_FAILED: - return LOCK_FAILED; - case VIRUS_DETECTED: - return VIRUS_DETECTED; - case CANNOT_CREATE_FILE: - return CANNOT_CREATE_FILE; - case QUOTA_EXCEEDED: - return QUOTA_EXCEEDED; - default: - return UNKNOWN; - } + yield UNKNOWN; + } + case LOCK_FAILED -> LOCK_FAILED; + case VIRUS_DETECTED -> VIRUS_DETECTED; + case CANNOT_CREATE_FILE -> CANNOT_CREATE_FILE; + case QUOTA_EXCEEDED -> QUOTA_EXCEEDED; + default -> UNKNOWN; + }; } } diff --git a/app/src/main/java/com/owncloud/android/features/FeatureItem.java b/app/src/main/java/com/owncloud/android/features/FeatureItem.java index 63f4170..a389e9c 100644 --- a/app/src/main/java/com/owncloud/android/features/FeatureItem.java +++ b/app/src/main/java/com/owncloud/android/features/FeatureItem.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018-2020 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.features; diff --git a/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java b/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java index d00db54..0e367ca 100644 --- a/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java +++ b/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2014 David A. Velasco * SPDX-FileCopyrightText: 2012 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.files; @@ -23,6 +23,7 @@ import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.network.WalledCheckCache; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; +import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -48,6 +49,7 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { @Inject Clock clock; @Inject ViewThemeUtils viewThemeUtils; @Inject WalledCheckCache walledCheckCache; + @Inject SyncedFolderProvider syncedFolderProvider; /** * Receives broadcast intent reporting that the system was just boot up. * @@ -60,7 +62,8 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { AndroidInjection.inject(this, context); if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { - MainApp.initSyncOperations(preferences, + MainApp.initSyncOperations(context, + preferences, uploadsStorageManager, accountManager, connectivityService, @@ -68,7 +71,9 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { backgroundJobManager, clock, viewThemeUtils, - walledCheckCache); + walledCheckCache, + syncedFolderProvider + ); MainApp.initContactsBackup(accountManager, backgroundJobManager); } else { Log_OC.d(TAG, "Getting wrong intent: " + intent.getAction()); diff --git a/app/src/main/java/com/owncloud/android/files/CreateFileFromTemplateOperation.java b/app/src/main/java/com/owncloud/android/files/CreateFileFromTemplateOperation.java index 59b1449..5e146a8 100644 --- a/app/src/main/java/com/owncloud/android/files/CreateFileFromTemplateOperation.java +++ b/app/src/main/java/com/owncloud/android/files/CreateFileFromTemplateOperation.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.files; diff --git a/app/src/main/java/com/owncloud/android/files/FetchTemplateOperation.java b/app/src/main/java/com/owncloud/android/files/FetchTemplateOperation.java index eb79c5e..e16fafa 100644 --- a/app/src/main/java/com/owncloud/android/files/FetchTemplateOperation.java +++ b/app/src/main/java/com/owncloud/android/files/FetchTemplateOperation.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.files; diff --git a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 38e855f..3b9199e 100644 --- a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2019-2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas * SPDX-FileCopyrightText: 2020 Andy Scherzinger @@ -9,7 +9,7 @@ * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2014-2016 David A. Velasco * SPDX-FileCopyrightText: 2012 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.files; @@ -22,7 +22,9 @@ import com.nextcloud.client.account.User; import com.nextcloud.client.editimage.EditImageActivity; import com.nextcloud.client.jobs.download.FileDownloadHelper; import com.nextcloud.client.jobs.upload.FileUploadHelper; +import com.nextcloud.ui.fileactions.FileAction; import com.nextcloud.utils.EditorUtils; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -36,6 +38,7 @@ import com.owncloud.android.utils.NextcloudServer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -169,10 +172,17 @@ public class FileMenuFilter { filterLock(toHide, fileLockingEnabled); filterUnlock(toHide, fileLockingEnabled); filterPinToHome(toHide); + filterRetry(toHide); + filterPermissionActions(toHide); return toHide; } + private void filterPermissionActions(List toHide) { + final var actionsToHide = FileAction.Companion.getActionsToHide(new HashSet<>(files)); + toHide.addAll(actionsToHide); + } + private void filterShareFile(List toHide, OCCapability capability) { if (!isSingleSelection() || containsEncryptedFile() || hasEncryptedParent() || @@ -183,9 +193,24 @@ public class FileMenuFilter { } private void filterSendFiles(List toHide, boolean inSingleFileFragment) { - if ((overflowMenu || SEND_OFF.equalsIgnoreCase(context.getString(R.string.send_files_to_other_apps)) || containsEncryptedFile()) || - (!inSingleFileFragment && (isSingleSelection() || !allFileDown())) || - !toHide.contains(R.id.action_send_share_file)) { + boolean sendFilesNotSupported = context != null && !MDMConfig.INSTANCE.sendFilesSupport(context); + boolean hasEncryptedFile = containsEncryptedFile(); + boolean isSingleSelection = isSingleSelection(); + boolean allFilesNotDown = !allFileDown(); + + if (sendFilesNotSupported) { + toHide.add(R.id.action_send_file); + return; + } + + if (overflowMenu || hasEncryptedFile) { + toHide.add(R.id.action_send_file); + return; + } + + if (!inSingleFileFragment && (isSingleSelection || allFilesNotDown)) { + toHide.add(R.id.action_send_file); + } else if (!toHide.contains(R.id.action_send_share_file)) { toHide.add(R.id.action_send_file); } } @@ -260,6 +285,12 @@ public class FileMenuFilter { } } + private void filterRetry(List toHide) { + if (!files.iterator().next().isOfflineOperation()) { + toHide.add(R.id.action_retry); + } + } + private void filterEdit( List toHide, OCCapability capability @@ -390,8 +421,10 @@ public class FileMenuFilter { } private boolean anyFileDownloading() { + final var fileDownloadHelper = FileDownloadHelper.Companion.instance(); + for (OCFile file : files) { - if (FileDownloadHelper.Companion.instance().isDownloading(user, file)) { + if (fileDownloadHelper.isDownloading(user, file)) { return true; } } @@ -416,13 +449,11 @@ public class FileMenuFilter { } private boolean isShareWithUsersAllowed() { - return context != null && - context.getResources().getBoolean(R.bool.share_with_users_feature); + return context != null && MDMConfig.INSTANCE.shareViaUser(context); } private boolean isShareViaLinkAllowed() { - return context != null && - context.getResources().getBoolean(R.bool.share_via_link_feature); + return context != null && MDMConfig.INSTANCE.shareViaLink(context); } private boolean isSingleSelection() { @@ -457,7 +488,7 @@ public class FileMenuFilter { } private boolean isGroupFolder() { - return files.iterator().next().isGroupFolder(); + return files.iterator().next().mounted(); } private boolean hasEncryptedParent() { diff --git a/app/src/main/java/com/owncloud/android/files/StreamMediaFileOperation.java b/app/src/main/java/com/owncloud/android/files/StreamMediaFileOperation.java index 7545fcf..6d12aed 100644 --- a/app/src/main/java/com/owncloud/android/files/StreamMediaFileOperation.java +++ b/app/src/main/java/com/owncloud/android/files/StreamMediaFileOperation.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.files; diff --git a/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java b/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java index d0d3fde..b19e5eb 100644 --- a/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java +++ b/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java @@ -1,11 +1,11 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2017-2018 Andy Scherzinger * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.files.services; @@ -184,10 +184,7 @@ public class IndexedForest { } private void removeDescendants(Node removed) { - Iterator> childrenIt = removed.getChildren().iterator(); - Node child = null; - while (childrenIt.hasNext()) { - child = childrenIt.next(); + for (Node child : removed.getChildren()) { mMap.remove(child.getKey()); removeDescendants(child); } @@ -218,14 +215,7 @@ public class IndexedForest { * @param accountName */ public void remove(String accountName){ - Iterator it = mMap.keySet().iterator(); - while (it.hasNext()) { - String key = it.next(); - Log_OC.d("IndexedForest", "Number of pending downloads= " + mMap.size()); - if (key.startsWith(accountName)) { - mMap.remove(key); - } - } + mMap.keySet().removeIf(key -> key.startsWith(accountName)); } /** diff --git a/app/src/main/java/com/owncloud/android/files/services/NameCollisionPolicy.java b/app/src/main/java/com/owncloud/android/files/services/NameCollisionPolicy.java index cab2f10..41ab166 100644 --- a/app/src/main/java/com/owncloud/android/files/services/NameCollisionPolicy.java +++ b/app/src/main/java/com/owncloud/android/files/services/NameCollisionPolicy.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2021 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.files.services; diff --git a/app/src/main/java/com/owncloud/android/media/MediaControlView.java b/app/src/main/java/com/owncloud/android/media/MediaControlView.java deleted file mode 100644 index 898b220..0000000 --- a/app/src/main/java/com/owncloud/android/media/MediaControlView.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2023 Alper Ozturk - * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas - * SPDX-FileCopyrightText: 2018-2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-FileCopyrightText: 2018 Andy Scherzinger - * SPDX-FileCopyrightText: 2015 ownCloud Inc. - * SPDX-FileCopyrightText: 2013 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later - */ -package com.owncloud.android.media; - -import android.content.Context; -import android.media.MediaPlayer; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.LinearLayout; -import android.widget.MediaController.MediaPlayerControl; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; - -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.databinding.MediaControlBinding; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.utils.theme.ViewThemeUtils; - -import java.util.Formatter; -import java.util.Locale; - -import javax.inject.Inject; - -/** - * View containing controls for a {@link MediaPlayer}. - *

    - * Holds buttons "play / pause", "rewind", "fast forward" and a progress slider. - *

    - * It synchronizes itself with the state of the {@link MediaPlayer}. - */ -public class MediaControlView extends LinearLayout implements OnClickListener, OnSeekBarChangeListener { - private static final String TAG = MediaControlView.class.getSimpleName(); - private static final int SHOW_PROGRESS = 1; - - private MediaPlayerControl playerControl; - private final MediaControlBinding binding; - private boolean isDragging; - - @Inject - ViewThemeUtils viewThemeUtils; - - public MediaControlView(Context context, AttributeSet attrs) { - super(context, attrs); - - MainApp.getAppComponent().inject(this); - - LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - binding = MediaControlBinding.inflate(inflate, this, true); - initControllerView(); - - setFocusable(true); - setFocusableInTouchMode(true); - setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); - requestFocus(); - } - - @Override - public void onFinishInflate() { - super.onFinishInflate(); - } - - public void setMediaPlayer(MediaPlayerControl player) { - playerControl = player; - handler.sendEmptyMessage(SHOW_PROGRESS); - handler.postDelayed(() -> { - updatePausePlay(); - setProgress(); - }, 100); - } - - public void stopMediaPlayerMessages() { - handler.removeMessages(SHOW_PROGRESS); - } - - private void initControllerView() { - binding.playBtn.requestFocus(); - binding.playBtn.setOnClickListener(this); - - binding.forwardBtn.setOnClickListener(this); - - binding.rewindBtn.setOnClickListener(this); - - viewThemeUtils.platform.themeHorizontalSeekBar(binding.progressBar); - binding.progressBar.setOnSeekBarChangeListener(this); - binding.progressBar.setMax(1000); - } - - /** - * Disable pause or seek buttons if the stream cannot be paused or seeked. - * This requires the control interface to be a MediaPlayerControlExt - */ - private void disableUnsupportedButtons() { - try { - if (binding != null) { - if (!playerControl.canPause()) { - binding.playBtn.setEnabled(false); - } - if (!playerControl.canSeekBackward()) { - binding.rewindBtn.setEnabled(false); - } - if (!playerControl.canSeekForward()) { - binding.forwardBtn.setEnabled(false); - } - } - - } catch (IncompatibleClassChangeError ex) { - // We were given an old version of the interface, that doesn't have - // the canPause/canSeekXYZ methods. This is OK, it just means we - // assume the media can be paused and seeked, and so we don't disable - // the buttons. - Log_OC.i(TAG, "Old media interface detected"); - } - } - - private final Handler handler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - if (msg.what == SHOW_PROGRESS) { - updatePausePlay(); - int pos = setProgress(); - if (!isDragging) { - sendMessageDelayed(obtainMessage(SHOW_PROGRESS), 1000 - (pos % 1000)); - } - } - } - }; - - private String formatTime(int timeMs) { - int totalSeconds = timeMs / 1000; - - int seconds = totalSeconds % 60; - int minutes = (totalSeconds / 60) % 60; - int hours = totalSeconds / 3600; - - final StringBuilder mFormatBuilder = new StringBuilder(); - final Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); - if (hours > 0) { - return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); - } else { - return mFormatter.format("%02d:%02d", minutes, seconds).toString(); - } - } - - private int setProgress() { - if (playerControl == null || isDragging) { - return 0; - } - int position = playerControl.getCurrentPosition(); - int duration = playerControl.getDuration(); - if (binding != null) { - if (duration > 0) { - // use long to avoid overflow - long pos = 1000L * position / duration; - binding.progressBar.setProgress((int) pos); - } - int percent = playerControl.getBufferPercentage(); - binding.progressBar.setSecondaryProgress(percent * 10); - - String endTime = duration > 0 ? formatTime(duration) : "--:--"; - binding.totalTimeText.setText(endTime); - binding.currentTimeText.setText(formatTime(position)); - } - - return position; - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - int keyCode = event.getKeyCode(); - final boolean uniqueDown = event.getRepeatCount() == 0 - && event.getAction() == KeyEvent.ACTION_DOWN; - if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK - || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE - || keyCode == KeyEvent.KEYCODE_SPACE) { - if (uniqueDown) { - doPauseResume(); - //show(sDefaultTimeout); - if (binding != null) { - binding.playBtn.requestFocus(); - } - } - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { - if (uniqueDown && !playerControl.isPlaying()) { - playerControl.start(); - updatePausePlay(); - } - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP - || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { - if (uniqueDown && playerControl.isPlaying()) { - playerControl.pause(); - updatePausePlay(); - } - return true; - } - - return super.dispatchKeyEvent(event); - } - - public void updatePausePlay() { - if (binding == null) { - return; - } - - if (playerControl.isPlaying()) { - binding.playBtn.setImageResource(android.R.drawable.ic_media_pause); - } else { - binding.playBtn.setImageResource(android.R.drawable.ic_media_play); - } - - final boolean canSeekFfd = playerControl.canSeekForward(); - if (canSeekFfd) { - binding.forwardBtn.setVisibility(View.VISIBLE); - } else { - binding.forwardBtn.setVisibility(View.INVISIBLE); - } - - final boolean canSeekBwd = playerControl.canSeekBackward(); - if (canSeekBwd) { - binding.rewindBtn.setVisibility(View.VISIBLE); - } else { - binding.rewindBtn.setVisibility(View.INVISIBLE); - } - } - - private void doPauseResume() { - if (playerControl.isPlaying()) { - playerControl.pause(); - } else { - playerControl.start(); - } - updatePausePlay(); - } - - @Override - public void setEnabled(boolean enabled) { - if(binding!=null){ - binding.playBtn.setEnabled(enabled); - binding.forwardBtn.setEnabled(enabled); - binding.rewindBtn.setEnabled(enabled); - binding.progressBar.setEnabled(enabled); - } - - disableUnsupportedButtons(); - super.setEnabled(enabled); - } - - @Override - public void onClick(View v) { - int pos; - boolean playing = playerControl.isPlaying(); - int id = v.getId(); - - if (id == R.id.playBtn) { - doPauseResume(); - } else if (id == R.id.rewindBtn) { - pos = playerControl.getCurrentPosition(); - pos -= 5000; - playerControl.seekTo(pos); - if (!playing) { - playerControl.pause(); // necessary in some 2.3.x devices - } - setProgress(); - } else if (id == R.id.forwardBtn) { - pos = playerControl.getCurrentPosition(); - pos += 15000; - playerControl.seekTo(pos); - if (!playing) { - playerControl.pause(); // necessary in some 2.3.x devices - } - setProgress(); - } - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (!fromUser) { - // We're not interested in programmatically generated changes to - // the progress bar's position. - return; - } - - long duration = playerControl.getDuration(); - long newPosition = (duration * progress) / 1000L; - playerControl.seekTo((int) newPosition); - binding.currentTimeText.setText(formatTime((int) newPosition)); - } - - /** - * Called in devices with touchpad when the user starts to adjust the position of the seekbar's thumb. - * - * Will be followed by several onProgressChanged notifications. - */ - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - isDragging = true; // monitors the duration of dragging - handler.removeMessages(SHOW_PROGRESS); // grants no more updates with media player progress while dragging - } - - /** - * Called in devices with touchpad when the user finishes the adjusting of the seekbar. - */ - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - isDragging = false; - setProgress(); - updatePausePlay(); - handler.sendEmptyMessage(SHOW_PROGRESS); // grants future updates with media player progress - } - - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - event.setClassName(MediaControlView.class.getName()); - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.setClassName(MediaControlView.class.getName()); - } -} diff --git a/app/src/main/java/com/owncloud/android/media/MediaControlView.kt b/app/src/main/java/com/owncloud/android/media/MediaControlView.kt new file mode 100644 index 0000000..217c1c1 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/media/MediaControlView.kt @@ -0,0 +1,355 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas + * SPDX-FileCopyrightText: 2018-2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2019 Chris Narkiewicz + * SPDX-FileCopyrightText: 2018 Andy Scherzinger + * SPDX-FileCopyrightText: 2015 ownCloud Inc. + * SPDX-FileCopyrightText: 2013 David A. Velasco + * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + */ +package com.owncloud.android.media + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.os.Message +import android.util.AttributeSet +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityNodeInfo +import android.widget.LinearLayout +import android.widget.SeekBar +import android.widget.SeekBar.OnSeekBarChangeListener +import androidx.core.content.ContextCompat +import androidx.media3.common.Player +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.databinding.MediaControlBinding +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.theme.ViewThemeUtils +import java.util.Formatter +import java.util.Locale +import javax.inject.Inject + +/** + * View containing controls for a MediaPlayer. + * + * + * Holds buttons "play / pause", "rewind", "fast forward" and a progress slider. + * + * + * It synchronizes itself with the state of the MediaPlayer. + */ +class MediaControlView(context: Context, attrs: AttributeSet?) : + LinearLayout(context, attrs), + View.OnClickListener, + OnSeekBarChangeListener { + + private var playerControl: Player? = null + private var binding: MediaControlBinding + private var isDragging = false + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + public override fun onFinishInflate() { + super.onFinishInflate() + } + + @Suppress("MagicNumber") + fun setMediaPlayer(player: Player?) { + playerControl = player + handler.sendEmptyMessage(SHOW_PROGRESS) + + handler.postDelayed({ + updatePausePlay() + setProgress() + }, 100) + } + + @Suppress("MagicNumber") + private fun initControllerView() { + binding.playBtn.requestFocus() + + binding.playBtn.setOnClickListener(this) + binding.forwardBtn.setOnClickListener(this) + binding.rewindBtn.setOnClickListener(this) + + binding.progressBar.run { + viewThemeUtils.platform.themeHorizontalSeekBar(this) + setMax(1000) + } + + binding.progressBar.setOnSeekBarChangeListener(this) + + viewThemeUtils.material.run { + colorMaterialButtonPrimaryTonal(binding.rewindBtn) + colorMaterialButtonPrimaryTonal(binding.playBtn) + colorMaterialButtonPrimaryTonal(binding.forwardBtn) + } + } + + /** + * Disable pause or seek buttons if the stream cannot be paused or seeked. + * This requires the control interface to be a MediaPlayerControlExt + */ + private fun disableUnsupportedButtons() { + try { + if (playerControl?.isCommandAvailable(Player.COMMAND_PLAY_PAUSE)?.not() == true) { + binding.playBtn.isEnabled = false + } + + if (playerControl?.isCommandAvailable(Player.COMMAND_SEEK_BACK)?.not() == true) { + binding.rewindBtn.isEnabled = false + } + if (playerControl?.isCommandAvailable(Player.COMMAND_SEEK_FORWARD)?.not() == true) { + binding.forwardBtn.isEnabled = false + } + } catch (ex: IncompatibleClassChangeError) { + // We were given an old version of the interface, that doesn't have + // the canPause/canSeekXYZ methods. This is OK, it just means we + // assume the media can be paused and seeked, and so we don't disable + // the buttons. + Log_OC.i(TAG, "Old media interface detected") + } + } + + @Suppress("MagicNumber") + private val handler: Handler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + if (msg.what == SHOW_PROGRESS) { + updatePausePlay() + val pos = setProgress() + + if (!isDragging) { + sendMessageDelayed(obtainMessage(SHOW_PROGRESS), (1000 - pos % 1000)) + } + } + } + } + + init { + MainApp.getAppComponent().inject(this) + + val inflate = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + binding = MediaControlBinding.inflate(inflate, this, true) + initControllerView() + isFocusable = true + setFocusableInTouchMode(true) + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS) + requestFocus() + } + + @Suppress("MagicNumber") + private fun formatTime(timeMs: Long): String { + val totalSeconds = timeMs / 1000 + val seconds = totalSeconds % 60 + val minutes = totalSeconds / 60 % 60 + val hours = totalSeconds / 3600 + val mFormatBuilder = StringBuilder() + val mFormatter = Formatter(mFormatBuilder, Locale.getDefault()) + return if (hours > 0) { + mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString() + } else { + mFormatter.format("%02d:%02d", minutes, seconds).toString() + } + } + + @Suppress("MagicNumber") + private fun setProgress(): Long { + var position = 0L + if (playerControl == null || isDragging) { + position = 0 + } + + playerControl?.let { playerControl -> + position = playerControl.currentPosition + val duration = playerControl.duration + if (duration > 0) { + // use long to avoid overflow + val pos = 1000L * position / duration + binding.progressBar.progress = pos.toInt() + } + val percent = playerControl.bufferedPercentage + binding.progressBar.setSecondaryProgress(percent * 10) + val endTime = if (duration > 0) formatTime(duration) else "--:--" + binding.totalTimeText.text = endTime + binding.currentTimeText.text = formatTime(position) + } + + return position + } + + @Suppress("ReturnCount") + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + val keyCode = event.keyCode + val uniqueDown = (event.repeatCount == 0 && event.action == KeyEvent.ACTION_DOWN) + + when (keyCode) { + KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_SPACE -> { + if (uniqueDown) { + doPauseResume() + // show(sDefaultTimeout); + binding.playBtn.requestFocus() + } + return true + } + + KeyEvent.KEYCODE_MEDIA_PLAY -> { + if (uniqueDown && playerControl?.playWhenReady == false) { + playerControl?.play() + updatePausePlay() + } + return true + } + + KeyEvent.KEYCODE_MEDIA_STOP, + KeyEvent.KEYCODE_MEDIA_PAUSE + -> { + if (uniqueDown && playerControl?.playWhenReady == true) { + playerControl?.pause() + updatePausePlay() + } + return true + } + + else -> return super.dispatchKeyEvent(event) + } + } + + fun updatePausePlay() { + binding.playBtn.icon = ContextCompat.getDrawable( + context, + // use isPlaying instead of playWhenReady + // it represents only the play/pause state + // which is needed to show play/pause icons + if (playerControl?.isPlaying == true) { + R.drawable.ic_pause + } else { + R.drawable.ic_play + } + ) + binding.forwardBtn.visibility = if (playerControl?.isCommandAvailable(Player.COMMAND_SEEK_FORWARD) == true) { + VISIBLE + } else { + INVISIBLE + } + binding.rewindBtn.visibility = if (playerControl?.isCommandAvailable(Player.COMMAND_SEEK_BACK) == true) { + VISIBLE + } else { + INVISIBLE + } + } + + private fun doPauseResume() { + playerControl?.run { + if (playWhenReady) { + pause() + } else { + play() + } + } + updatePausePlay() + } + + override fun setEnabled(enabled: Boolean) { + binding.playBtn.setEnabled(enabled) + binding.forwardBtn.setEnabled(enabled) + binding.rewindBtn.setEnabled(enabled) + binding.progressBar.setEnabled(enabled) + + disableUnsupportedButtons() + + super.setEnabled(enabled) + } + + @Suppress("MagicNumber") + override fun onClick(v: View) { + playerControl?.let { playerControl -> + val playing = playerControl.playWhenReady + val id = v.id + + when (id) { + R.id.playBtn -> { + doPauseResume() + } + + R.id.rewindBtn -> { + playerControl.seekBack() + if (!playing) { + playerControl.pause() // necessary in some 2.3.x devices + } + setProgress() + } + + R.id.forwardBtn -> { + playerControl.seekForward() + if (!playing) { + playerControl.pause() // necessary in some 2.3.x devices + } + + setProgress() + } + + else -> { + } + } + } + } + + @Suppress("MagicNumber") + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (!fromUser) { + // We're not interested in programmatically generated changes to + // the progress bar's position. + return + } + + playerControl?.let { playerControl -> + val duration = playerControl.duration + val newPosition = duration * progress / 1000L + playerControl.seekTo(newPosition) + binding.currentTimeText.text = formatTime(newPosition) + } + } + + /** + * Called in devices with touchpad when the user starts to adjust the position of the seekbar's thumb. + * + * Will be followed by several onProgressChanged notifications. + */ + override fun onStartTrackingTouch(seekBar: SeekBar) { + isDragging = true // monitors the duration of dragging + handler.removeMessages(SHOW_PROGRESS) // grants no more updates with media player progress while dragging + } + + /** + * Called in devices with touchpad when the user finishes the adjusting of the seekbar. + */ + override fun onStopTrackingTouch(seekBar: SeekBar) { + isDragging = false + setProgress() + updatePausePlay() + handler.sendEmptyMessage(SHOW_PROGRESS) // grants future updates with media player progress + } + + override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) { + super.onInitializeAccessibilityEvent(event) + event.setClassName(MediaControlView::class.java.getName()) + } + + override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) { + super.onInitializeAccessibilityNodeInfo(info) + info.setClassName(MediaControlView::class.java.getName()) + } + + companion object { + private val TAG = MediaControlView::class.java.getSimpleName() + private const val SHOW_PROGRESS = 1 + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/CheckCurrentCredentialsOperation.java b/app/src/main/java/com/owncloud/android/operations/CheckCurrentCredentialsOperation.java index c8a69e3..7304ed2 100644 --- a/app/src/main/java/com/owncloud/android/operations/CheckCurrentCredentialsOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CheckCurrentCredentialsOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2018 Andy Scherzinger * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2016 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/CommentFileOperation.java b/app/src/main/java/com/owncloud/android/operations/CommentFileOperation.java index fdf16a8..5984d17 100644 --- a/app/src/main/java/com/owncloud/android/operations/CommentFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CommentFileOperation.java @@ -3,11 +3,11 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; -import com.owncloud.android.lib.common.OwnCloudClient; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -16,7 +16,7 @@ import com.owncloud.android.lib.resources.comments.CommentFileRemoteOperation; /** * Comment file */ -public class CommentFileOperation extends RemoteOperation { +public class CommentFileOperation extends RemoteOperation { private final String message; private final long fileId; @@ -37,8 +37,8 @@ public class CommentFileOperation extends RemoteOperation { * @param client Client object to communicate with the remote ownCloud server. */ @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = new CommentFileRemoteOperation(message, fileId).execute(client); + public RemoteOperationResult run(NextcloudClient client) { + RemoteOperationResult result = new CommentFileRemoteOperation(message, fileId).execute(client); if (!result.isSuccess()) { Log_OC.e(this, "File with Id " + fileId + " could not be commented"); diff --git a/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java b/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java index eba4565..bd7d21a 100644 --- a/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2019 Andy Scherzinger * SPDX-FileCopyrightText: 2012-2014 ownCloud Inc. * SPDX-FileCopyrightText: 2014 Jorge Antonio Diaz-Benito Soriano - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -64,6 +64,19 @@ public class CopyFileOperation extends SyncOperation { if (file.isFolder()) { targetPath += OCFile.PATH_SEPARATOR; } + + // auto rename, to allow copy + if (targetPath.equals(srcPath)) { + if (file.isFolder()) { + targetPath = targetParentPath + file.getFileName(); + } + targetPath = UploadFileOperation.getNewAvailableRemotePath(client, targetPath, null, false); + + if (file.isFolder()) { + targetPath += OCFile.PATH_SEPARATOR; + } + } + RemoteOperationResult result = new CopyFileRemoteOperation(srcPath, targetPath, false).execute(client); /// 3. local copy diff --git a/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java index ad16496..c2d6e39 100644 --- a/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2012 David A. Velasco * SPDX-FileCopyrightText: 2015 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -44,6 +44,7 @@ import java.io.File; import java.util.UUID; import androidx.annotation.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; import static com.owncloud.android.datamodel.OCFile.ROOT_PATH; @@ -109,6 +110,10 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper } } + @SuppressFBWarnings( + value = "EXS_EXCEPTION_SOFTENING_NO_CONSTRAINTS", + justification = "Converting checked exception to runtime is acceptable in this context" + ) private RemoteOperationResult encryptedCreateV1(OCFile parent, OwnCloudClient client) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context); String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY); @@ -186,20 +191,24 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper } } - RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(encryptedRemotePath) + final var remoteFolderOperationResult = new ReadFolderRemoteOperation(encryptedRemotePath) .execute(client); - createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0); - OCFile newDir = createRemoteFolderOcFile(parent, filename, createdRemoteFolder); - getStorageManager().saveFile(newDir); + if (remoteFolderOperationResult.isSuccess() && remoteFolderOperationResult.getData().get(0) instanceof RemoteFile remoteFile) { + createdRemoteFolder = remoteFile; + OCFile newDir = createRemoteFolderOcFile(parent, filename, createdRemoteFolder); + getStorageManager().saveFile(newDir); - RemoteOperationResult encryptionOperationResult = new ToggleEncryptionRemoteOperation( - newDir.getLocalId(), - newDir.getRemotePath(), - true) - .execute(client); + final var encryptionOperationResult = new ToggleEncryptionRemoteOperation( + newDir.getLocalId(), + newDir.getRemotePath(), + true) + .execute(client); - if (!encryptionOperationResult.isSuccess()) { + if (!encryptionOperationResult.isSuccess()) { + throw new RuntimeException("Error creating encrypted subfolder!"); + } + } else { throw new RuntimeException("Error creating encrypted subfolder!"); } } else { @@ -243,6 +252,10 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper } } + @SuppressFBWarnings( + value = "EXS_EXCEPTION_SOFTENING_NO_CONSTRAINTS", + justification = "Converting checked exception to runtime is acceptable in this context" + ) private RemoteOperationResult encryptedCreateV2(OCFile parent, OwnCloudClient client) { String token = null; Boolean metadataExists; @@ -324,20 +337,24 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper throw new RuntimeException("Could not unlock folder!"); } - RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(encryptedRemotePath) + final var remoteFolderOperationResult = new ReadFolderRemoteOperation(encryptedRemotePath) .execute(client); - createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0); - OCFile newDir = createRemoteFolderOcFile(parent, filename, createdRemoteFolder); - getStorageManager().saveFile(newDir); + if (remoteFolderOperationResult.isSuccess() && remoteFolderOperationResult.getData().get(0) instanceof RemoteFile remoteFile) { + createdRemoteFolder = remoteFile; + OCFile newDir = createRemoteFolderOcFile(parent, filename, createdRemoteFolder); + getStorageManager().saveFile(newDir); - RemoteOperationResult encryptionOperationResult = new ToggleEncryptionRemoteOperation( - newDir.getLocalId(), - newDir.getRemotePath(), - true) - .execute(client); + final var encryptionOperationResult = new ToggleEncryptionRemoteOperation( + newDir.getLocalId(), + newDir.getRemotePath(), + true) + .execute(client); - if (!encryptionOperationResult.isSuccess()) { + if (!encryptionOperationResult.isSuccess()) { + throw new RuntimeException("Error creating encrypted subfolder!"); + } + } else { throw new RuntimeException("Error creating encrypted subfolder!"); } } else { diff --git a/app/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java b/app/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java index 2c9377c..9dcbaa1 100644 --- a/app/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2016 David A. Velasco * SPDX-FileCopyrightText: 2014 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -75,11 +75,7 @@ public class CreateShareViaLinkOperation extends SyncOperation { private void updateData(OCShare share) { // Update DB with the response share.setPath(path); - if (path.endsWith(FileUtils.PATH_SEPARATOR)) { - share.setFolder(true); - } else { - share.setFolder(false); - } + share.setFolder(path.endsWith(FileUtils.PATH_SEPARATOR)); getStorageManager().saveShare(share); diff --git a/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java b/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java index bcae519..28e7320 100644 --- a/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -17,6 +17,7 @@ import com.nextcloud.client.account.User; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ClientFactoryImpl; import com.nextcloud.common.NextcloudClient; +import com.nextcloud.utils.extensions.DecryptedUserExtensionsKt; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -55,12 +56,14 @@ public class CreateShareWithShareeOperation extends SyncOperation { private String label; private final Context context; private final User user; + private String attributes; private ArbitraryDataProvider arbitraryDataProvider; private static final Set supportedShareTypes = new HashSet<>(Arrays.asList(ShareType.USER, ShareType.GROUP, ShareType.FEDERATED, + ShareType.FEDERATED_GROUP, ShareType.EMAIL, ShareType.ROOM, ShareType.CIRCLE)); @@ -84,6 +87,7 @@ public class CreateShareWithShareeOperation extends SyncOperation { String sharePassword, long expirationDateInMillis, boolean hideFileDownload, + String attributes, FileDataStorageManager storageManager, Context context, User user, @@ -104,6 +108,7 @@ public class CreateShareWithShareeOperation extends SyncOperation { this.context = context; this.user = user; this.arbitraryDataProvider = arbitraryDataProvider; + this.attributes = attributes; } @Override @@ -123,7 +128,7 @@ public class CreateShareWithShareeOperation extends SyncOperation { try { String publicKey = EncryptionUtils.getPublicKey(user, shareeName, arbitraryDataProvider); - if (publicKey.equals("")) { + if (publicKey.isEmpty()) { NextcloudClient nextcloudClient = new ClientFactoryImpl(context).createNextcloudClient(user); RemoteOperationResult result = new GetPublicKeyRemoteOperation(shareeName).execute(nextcloudClient); if (result.isSuccess()) { @@ -155,7 +160,8 @@ public class CreateShareWithShareeOperation extends SyncOperation { false, sharePassword, permissions, - noteMessage + noteMessage, + attributes ); operation.setGetShareDetails(true); RemoteOperationResult shareResult = operation.execute(client); @@ -183,7 +189,7 @@ public class CreateShareWithShareeOperation extends SyncOperation { if (metadata == null) { String cert = EncryptionUtils.retrievePublicKeyForUser(user, context); metadata = new EncryptionUtilsV2().createDecryptedFolderMetadataFile(); - metadata.getUsers().add(new DecryptedUser(client.getUserId(), cert)); + metadata.getUsers().add(new DecryptedUser(client.getUserId(), cert, null)); metadataExists = false; } else { @@ -194,9 +200,12 @@ public class CreateShareWithShareeOperation extends SyncOperation { // add sharee to metadata String publicKey = EncryptionUtils.getPublicKey(user, shareeName, arbitraryDataProvider); + + String decryptedMetadataKey = DecryptedUserExtensionsKt.findMetadataKeyByUserId(metadata.getUsers(), shareeName); DecryptedFolderMetadataFile newMetadata = encryptionUtilsV2.addShareeToMetadata(metadata, shareeName, - publicKey); + publicKey, + decryptedMetadataKey); // upload metadata metadata.getMetadata().setCounter(newCounter); diff --git a/app/src/main/java/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java b/app/src/main/java/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java index f5812d5..c1a5f7e 100644 --- a/app/src/main/java/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2016-2017 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2014 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -70,7 +70,6 @@ public class DetectAuthenticationMethodOperation extends RemoteOperation { */ @Override protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = null; AuthenticationMethod authMethod = AuthenticationMethod.UNKNOWN; RemoteOperation operation = new ExistenceCheckRemoteOperation("", mContext, false); @@ -78,7 +77,7 @@ public class DetectAuthenticationMethodOperation extends RemoteOperation { client.setFollowRedirects(false); // try to access the root folder, following redirections but not SAML SSO redirections - result = operation.execute(client); + RemoteOperationResult result = operation.execute(client); String redirectedLocation = result.getRedirectedLocation(); while (!TextUtils.isEmpty(redirectedLocation) && !result.isIdPRedirection()) { client.setBaseUri(Uri.parse(result.getRedirectedLocation())); @@ -119,18 +118,13 @@ public class DetectAuthenticationMethodOperation extends RemoteOperation { } private String authenticationMethodToString(AuthenticationMethod value) { - switch (value) { - case NONE: - return "NONE"; - case BASIC_HTTP_AUTH: - return "BASIC_HTTP_AUTH"; - case BEARER_TOKEN: - return "BEARER_TOKEN"; - case SAML_WEB_SSO: - return "SAML_WEB_SSO"; - default: - return "UNKNOWN"; - } + return switch (value) { + case NONE -> "NONE"; + case BASIC_HTTP_AUTH -> "BASIC_HTTP_AUTH"; + case BEARER_TOKEN -> "BEARER_TOKEN"; + case SAML_WEB_SSO -> "SAML_WEB_SSO"; + default -> "UNKNOWN"; + }; } } diff --git a/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java index ec70fd4..928f437 100644 --- a/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java @@ -1,21 +1,25 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2020-2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2012 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; import android.content.Context; +import android.os.Handler; +import android.os.Looper; import android.text.TextUtils; import android.webkit.MimeTypeMap; import com.nextcloud.client.account.User; +import com.nextcloud.utils.extensions.ContextExtensionsKt; +import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -34,7 +38,6 @@ import com.owncloud.android.utils.FileExportUtils; import com.owncloud.android.utils.FileStorageUtils; import java.io.File; -import java.io.FileOutputStream; import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Iterator; @@ -60,11 +63,14 @@ public class DownloadFileOperation extends RemoteOperation { private DownloadType downloadType; private final WeakReference context; + + // CHECK: Is this still needed after conversion from Foreground Services to Worker? private Set dataTransferListeners = new HashSet<>(); + private long modificationTimestamp; private DownloadFileRemoteOperation downloadOperation; - private final AtomicBoolean cancellationRequested = new AtomicBoolean(false); + private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); public DownloadFileOperation(User user, OCFile file, @@ -163,13 +169,19 @@ public class DownloadFileOperation extends RemoteOperation { /// perform the download synchronized(cancellationRequested) { if (cancellationRequested.get()) { - return new RemoteOperationResult(new OperationCancelledException()); + return new RemoteOperationResult<>(new OperationCancelledException()); } } + final var isValidExtFilename = FileStorageUtils.isValidExtFilename(file.getFileName()); + if (!isValidExtFilename) { + mainThreadHandler.post(() -> ContextExtensionsKt.showToast(context.get(), R.string.download_download_invalid_local_file_name)); + return new RemoteOperationResult<>(RemoteOperationResult.ResultCode.INVALID_CHARACTER_IN_NAME); + } + Context operationContext = context.get(); if (operationContext == null) { - return new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); + return new RemoteOperationResult<>(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); } RemoteOperationResult result; @@ -184,10 +196,7 @@ public class DownloadFileOperation extends RemoteOperation { downloadOperation = new DownloadFileRemoteOperation(file.getRemotePath(), tmpFolder); if (downloadType == DownloadType.DOWNLOAD) { - Iterator listener = dataTransferListeners.iterator(); - while (listener.hasNext()) { - downloadOperation.addDatatransferProgressListener(listener.next()); - } + dataTransferListeners.forEach(downloadOperation::addDatatransferProgressListener); } result = downloadOperation.execute(client); diff --git a/app/src/main/java/com/owncloud/android/operations/DownloadType.kt b/app/src/main/java/com/owncloud/android/operations/DownloadType.kt index 812e673..fafa110 100644 --- a/app/src/main/java/com/owncloud/android/operations/DownloadType.kt +++ b/app/src/main/java/com/owncloud/android/operations/DownloadType.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations @@ -11,7 +11,5 @@ enum class DownloadType(var type: String) { DOWNLOAD("DOWNLOAD"), EXPORT("EXPORT"); - override fun toString(): String { - return type - } + override fun toString(): String = type } diff --git a/app/src/main/java/com/owncloud/android/operations/GetCapabilitiesOperation.java b/app/src/main/java/com/owncloud/android/operations/GetCapabilitiesOperation.java index b151a49..9e86d16 100644 --- a/app/src/main/java/com/owncloud/android/operations/GetCapabilitiesOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/GetCapabilitiesOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2020 Daniel Kesselberg * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt new file mode 100644 index 0000000..b3b35c5 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt @@ -0,0 +1,30 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+zetatom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations + +import com.nextcloud.android.lib.resources.files.FileDownloadLimit +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.operations.common.SyncOperation + +class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDataStorageManager) : + SyncOperation( + storageManager + ) { + override fun run(client: NextcloudClient): RemoteOperationResult> { + val token = share.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) + val operation = GetFilesDownloadLimitRemoteOperation(token) + + val result = operation.execute(client) + + return result + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/GetServerInfoOperation.java b/app/src/main/java/com/owncloud/android/operations/GetServerInfoOperation.java index 2f6cf72..5f402d0 100644 --- a/app/src/main/java/com/owncloud/android/operations/GetServerInfoOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/GetServerInfoOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2014 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -46,7 +46,7 @@ public class GetServerInfoOperation extends RemoteOperation { * TODO ugly dependency, get rid of it. */ public GetServerInfoOperation(String url, Context context) { - mUrl = trimWebdavSuffix(url); + mUrl = AuthenticatorUrlUtils.INSTANCE.trimWebdavSuffix(url); mContext = context; mResultData = new ServerInfo(); } @@ -77,7 +77,7 @@ public class GetServerInfoOperation extends RemoteOperation { // third: merge results if (detectAuthResult.isSuccess()) { mResultData.mAuthMethod = (AuthenticationMethod) detectAuthResult.getData().get(0); - ArrayList data = new ArrayList(); + ArrayList data = new ArrayList<>(); data.add(mResultData); result.setData(data); } else { @@ -95,24 +95,6 @@ public class GetServerInfoOperation extends RemoteOperation { return operation.execute(client); } - - private String trimWebdavSuffix(String url) { - String trimmedUrl = url; - if (trimmedUrl == null) { - trimmedUrl = ""; - } else { - if (trimmedUrl.endsWith("/")) { - trimmedUrl = trimmedUrl.substring(0, trimmedUrl.length() - 1); - } - if (trimmedUrl.toLowerCase(Locale.ROOT).endsWith(AuthenticatorUrlUtils.WEBDAV_PATH_4_0_AND_LATER)) { - trimmedUrl = trimmedUrl.substring(0, - trimmedUrl.length() - AuthenticatorUrlUtils.WEBDAV_PATH_4_0_AND_LATER.length()); - } - } - return trimmedUrl; - } - - private String normalizeProtocolPrefix(String url, boolean isSslConn) { if (!url.toLowerCase(Locale.ROOT).startsWith("http://") && !url.toLowerCase(Locale.ROOT).startsWith("https://")) { diff --git a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java deleted file mode 100644 index 2e9e18e..0000000 --- a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2021 Tobias Kaminsky - * SPDX-FileCopyrightText: 2015 ownCloud Inc. - * SPDX-FileCopyrightText: 2014-2015 María Asensio Valverde - * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later - */ -package com.owncloud.android.operations; - -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.operations.common.SyncOperation; - -import java.util.ArrayList; - -/** - * Provide a list shares for a specific file. - */ -public class GetSharesForFileOperation extends SyncOperation { - - private static final String TAG = GetSharesForFileOperation.class.getSimpleName(); - - private final String path; - private final boolean reshares; - private final boolean subfiles; - - /** - * Constructor - * - * @param path Path to file or folder - * @param reshares If set to false (default), only shares from the current user are returned If set to true, all - * shares from the given file are returned - * @param subfiles If set to false (default), lists only the folder being shared If set to true, all shared files - * within the folder are returned. - */ - public GetSharesForFileOperation(String path, - boolean reshares, - boolean subfiles, - FileDataStorageManager storageManager) { - super(storageManager); - - this.path = path; - this.reshares = reshares; - this.subfiles = subfiles; - } - - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - GetSharesForFileRemoteOperation operation = new GetSharesForFileRemoteOperation(path, - reshares, - subfiles); - RemoteOperationResult result = operation.execute(client); - - if (result.isSuccess()) { - - // Update DB with the response - Log_OC.d(TAG, "File = " + path + " Share list size " + result.getData().size()); - ArrayList shares = new ArrayList(); - for (Object obj : result.getData()) { - shares.add((OCShare) obj); - } - - getStorageManager().saveSharesDB(shares); - - } else if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) { - // no share on the file - remove local shares - getStorageManager().removeSharesForFile(path); - - } - - return result; - } - -} diff --git a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt new file mode 100644 index 0000000..47634dd --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2021 Tobias Kaminsky + * SPDX-FileCopyrightText: 2015 ownCloud Inc. + * SPDX-FileCopyrightText: 2014-2015 María Asensio Valverde + * SPDX-FileCopyrightText: 2015 David A. Velasco + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + */ +package com.owncloud.android.operations + +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.operations.common.SyncOperation + +/** + * Provide a list shares for a specific file. + */ +class GetSharesForFileOperation( + private val path: String, + private val reshares: Boolean, + private val subfiles: Boolean, + storageManager: FileDataStorageManager +) : SyncOperation(storageManager) { + + @Suppress("DEPRECATION", "NestedBlockDepth") + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult> { + val result = GetSharesForFileRemoteOperation(path, reshares, subfiles).execute(client) + + if (result.isSuccess) { + // Update DB with the response + val shares = result.resultData + Log_OC.d(TAG, "File = $path Share list size ${shares.size}") + + val capability = storageManager.getCapability(storageManager.user) + if (capability.filesDownloadLimit.isTrue && shares.any { it.shareType == ShareType.PUBLIC_LINK }) { + val downloadLimitResult = GetFilesDownloadLimitRemoteOperation(path, subfiles).execute(client) + if (downloadLimitResult.isSuccess) { + val downloadLimits = downloadLimitResult.resultData + downloadLimits.forEach { downloadLimit -> + shares.find { share -> + share.token == downloadLimit.token + }?.fileDownloadLimit = downloadLimit + } + } + } + + storageManager.saveSharesDB(shares) + } else if (result.code == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) { + // no share on the file - remove local shares + storageManager.removeSharesForFile(path) + } + + return result + } + + companion object { + private val TAG: String = GetSharesForFileOperation::class.java.simpleName + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/GetUserProfileOperation.java b/app/src/main/java/com/owncloud/android/operations/GetUserProfileOperation.java index 4aa248d..48de76b 100644 --- a/app/src/main/java/com/owncloud/android/operations/GetUserProfileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/GetUserProfileOperation.java @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2021 Chris Narkiewicz * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2016 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/MoveFileOperation.java b/app/src/main/java/com/owncloud/android/operations/MoveFileOperation.java index d50fe82..8e2d1d1 100644 --- a/app/src/main/java/com/owncloud/android/operations/MoveFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/MoveFileOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2014 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index e0f0538..66bd13a 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2019-2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2013 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -16,6 +16,7 @@ import com.google.gson.Gson; import com.nextcloud.android.lib.resources.directediting.DirectEditingObtainRemoteOperation; import com.nextcloud.client.account.User; import com.nextcloud.common.NextcloudClient; +import com.nextcloud.utils.extensions.StringExtensionsKt; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -35,9 +36,6 @@ import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; -import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.E2EVersion; import com.owncloud.android.lib.resources.users.GetPredefinedStatusesRemoteOperation; import com.owncloud.android.lib.resources.users.PredefinedStatus; @@ -77,10 +75,12 @@ public class RefreshFolderOperation extends RemoteOperation { public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED = RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED"; + private boolean isMetadataSyncWorkerRunning = false; + /** * Time stamp for the synchronization process in progress */ - private long mCurrentSyncTime; + private final long mCurrentSyncTime; /** * Remote folder to synchronize @@ -90,17 +90,17 @@ public class RefreshFolderOperation extends RemoteOperation { /** * Access to the local database */ - private FileDataStorageManager mStorageManager; + private final FileDataStorageManager fileDataStorageManager; /** * Account where the file to synchronize belongs */ - private User user; + private final User user; /** * Android context; necessary to send requests to the download service */ - private Context mContext; + private final Context mContext; /** * Files and folders contained in the synchronized folder after a successful operation @@ -121,12 +121,12 @@ public class RefreshFolderOperation extends RemoteOperation { * Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and * couldn't be copied automatically into it **/ - private Map mForgottenLocalFiles; + private final Map mForgottenLocalFiles; /** * 'True' means that this operation is part of a full account synchronization */ - private boolean mSyncFullAccount; + private final boolean mSyncFullAccount; /** * 'True' means that the remote folder changed and should be fetched @@ -136,14 +136,14 @@ public class RefreshFolderOperation extends RemoteOperation { /** * 'True' means that Etag will be ignored */ - private boolean mIgnoreETag; + private final boolean mIgnoreETag; /** * 'True' means that no share and no capabilities will be updated */ - private boolean mOnlyFileMetadata; + private final boolean mOnlyFileMetadata; - private List mFilesToSyncContents; + private final List mFilesToSyncContents; // this will be used for every file when 'folder synchronization' replaces 'folder download' @@ -169,7 +169,7 @@ public class RefreshFolderOperation extends RemoteOperation { mLocalFolder = folder; mCurrentSyncTime = currentSyncTime; mSyncFullAccount = syncFullAccount; - mStorageManager = dataStorageManager; + fileDataStorageManager = dataStorageManager; this.user = user; mContext = context; mForgottenLocalFiles = new HashMap<>(); @@ -179,6 +179,29 @@ public class RefreshFolderOperation extends RemoteOperation { mFilesToSyncContents = new Vector<>(); } + /** + * Returns RefreshFolderOperation for metadata sync worker + */ + public RefreshFolderOperation(OCFile folder, + FileDataStorageManager dataStorageManager, + User user, + Context context) { + mLocalFolder = folder; + mCurrentSyncTime = System.currentTimeMillis(); + mSyncFullAccount = false; + fileDataStorageManager = dataStorageManager; + this.user = user; + mContext = context; + mForgottenLocalFiles = new HashMap<>(); + mRemoteFolderChanged = false; + mIgnoreETag = false; + mOnlyFileMetadata = true; + mFilesToSyncContents = new Vector<>(); + + // since metadata worker working in background for sub-folders no need send folder refresh event + isMetadataSyncWorkerRunning = true; + } + public RefreshFolderOperation(OCFile folder, long currentSyncTime, boolean syncFullAccount, @@ -190,7 +213,7 @@ public class RefreshFolderOperation extends RemoteOperation { mLocalFolder = folder; mCurrentSyncTime = currentSyncTime; mSyncFullAccount = syncFullAccount; - mStorageManager = dataStorageManager; + fileDataStorageManager = dataStorageManager; this.user = user; mContext = context; mForgottenLocalFiles = new HashMap<>(); @@ -234,6 +257,11 @@ public class RefreshFolderOperation extends RemoteOperation { mConflictsFound = 0; mForgottenLocalFiles.clear(); + if (mLocalFolder == null) { + Log_OC.e(TAG, "Local folder is null, cannot run refresh folder operation"); + return new RemoteOperationResult<>(ResultCode.FILE_NOT_FOUND); + } + if (OCFile.ROOT_PATH.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount && !mOnlyFileMetadata) { updateOCVersion(client); updateUserProfile(); @@ -243,10 +271,10 @@ public class RefreshFolderOperation extends RemoteOperation { if (result.isSuccess()) { if (mRemoteFolderChanged) { - // TODO catch IllegalStateException, show properly to user result = fetchAndSyncRemoteFolder(client); } else { - mChildren = mStorageManager.getFolderContent(mLocalFolder, false); + Log_OC.d(TAG, "💾 Remote folder is not changed, getting folder content from database"); + mChildren = fileDataStorageManager.getFolderContent(mLocalFolder, false); } if (result.isSuccess()) { @@ -256,28 +284,36 @@ public class RefreshFolderOperation extends RemoteOperation { mLocalFolder.setEtag(""); } - mLocalFolder.setLastSyncDateForData(System.currentTimeMillis()); - mStorageManager.saveFile(mLocalFolder); + if (mLocalFolder != null) { + mLocalFolder.setLastSyncDateForData(System.currentTimeMillis()); + fileDataStorageManager.saveFile(mLocalFolder); + } else { + Log_OC.e(TAG, "Local folder is null, cannot set last sync date nor save file"); + result = new RemoteOperationResult<>(ResultCode.FILE_NOT_FOUND); + } } - if (!mSyncFullAccount && mRemoteFolderChanged) { - sendLocalBroadcast( - EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result - ); + if (!mSyncFullAccount && mRemoteFolderChanged && mLocalFolder != null && !isMetadataSyncWorkerRunning) { + sendLocalBroadcast(EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result); } - if (result.isSuccess() && !mSyncFullAccount && !mOnlyFileMetadata) { - refreshSharesForFolder(client); // share result is ignored + if (result.isSuccess() && result.getData() != null && !mSyncFullAccount && !mOnlyFileMetadata) { + final var remoteObject = result.getData(); + final ArrayList remoteFiles = new ArrayList<>(); + for (Object object: remoteObject) { + if (object instanceof RemoteFile remoteFile) { + remoteFiles.add(remoteFile); + } + } + + fileDataStorageManager.saveSharesFromRemoteFile(remoteFiles); } - if (!mSyncFullAccount) { - sendLocalBroadcast( - EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result - ); + if (!mSyncFullAccount && mLocalFolder != null && !isMetadataSyncWorkerRunning) { + sendLocalBroadcast(EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result); } return result; - } private void updateOCVersion(OwnCloudClient client) { @@ -293,7 +329,7 @@ public class RefreshFolderOperation extends RemoteOperation { try { NextcloudClient nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, mContext); - RemoteOperationResult result = new GetUserProfileOperation(mStorageManager).execute(nextcloudClient); + RemoteOperationResult result = new GetUserProfileOperation(fileDataStorageManager).execute(nextcloudClient); if (!result.isSuccess()) { Log_OC.w(TAG, "Couldn't update user profile from server"); } else { @@ -309,9 +345,9 @@ public class RefreshFolderOperation extends RemoteOperation { String oldDirectEditingEtag = arbitraryDataProvider.getValue(user, ArbitraryDataProvider.DIRECT_EDITING_ETAG); - RemoteOperationResult result = new GetCapabilitiesOperation(mStorageManager).execute(mContext); + RemoteOperationResult result = new GetCapabilitiesOperation(fileDataStorageManager).execute(mContext); if (result.isSuccess()) { - String newDirectEditingEtag = mStorageManager.getCapability(user.getAccountName()).getDirectEditingEtag(); + String newDirectEditingEtag = fileDataStorageManager.getCapability(user.getAccountName()).getDirectEditingEtag(); if (!oldDirectEditingEtag.equalsIgnoreCase(newDirectEditingEtag)) { updateDirectEditing(arbitraryDataProvider, newDirectEditingEtag); @@ -324,8 +360,8 @@ public class RefreshFolderOperation extends RemoteOperation { } private void updateDirectEditing(ArbitraryDataProvider arbitraryDataProvider, String newDirectEditingEtag) { - RemoteOperationResult result = new DirectEditingObtainRemoteOperation().execute(user, - mContext); + RemoteOperationResult result = + new DirectEditingObtainRemoteOperation().executeNextcloudClient(user, mContext); if (result.isSuccess()) { DirectEditing directEditing = result.getResultData(); @@ -364,7 +400,12 @@ public class RefreshFolderOperation extends RemoteOperation { private RemoteOperationResult checkForChanges(OwnCloudClient client) { mRemoteFolderChanged = true; - RemoteOperationResult result; + if (isMetadataSyncWorkerRunning) { + Log_OC.d(TAG, "Skipping eTag check since metadata worker already did"); + return new RemoteOperationResult<>(ResultCode.OK); + } + + RemoteOperationResult result; String remotePath = mLocalFolder.getRemotePath(); Log_OC.d(TAG, "Checking changes in " + user.getAccountName() + remotePath); @@ -373,19 +414,28 @@ public class RefreshFolderOperation extends RemoteOperation { result = new ReadFileRemoteOperation(remotePath).execute(client); if (result.isSuccess()) { - OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0)); - - if (!mIgnoreETag) { + if (!mIgnoreETag && result.getData().get(0) instanceof RemoteFile remoteFile) { // check if remote and local folder are different - String remoteFolderETag = remoteFolder.getEtag(); + String remoteFolderETag = remoteFile.getEtag(); if (remoteFolderETag != null) { - mRemoteFolderChanged = !(remoteFolderETag.equalsIgnoreCase(mLocalFolder.getEtag())); + String localFolderEtag = mLocalFolder.getEtag(); + mRemoteFolderChanged = !StringExtensionsKt.isNotBlankAndEquals(remoteFolderETag, localFolderEtag); + Log_OC.d( + TAG, + "📂 eTag check\n" + + " Path: " + remoteFile.getRemotePath() + "\n" + + " Local eTag: " + localFolderEtag + "\n" + + " Remote eTag: " + remoteFolderETag + "\n" + + " Changed: " + mRemoteFolderChanged + ); } else { Log_OC.e(TAG, "Checked " + user.getAccountName() + remotePath + ": No ETag received from server"); } + } else { + Log_OC.d(TAG, "Ignoring eTag. mRemoteFolderChanged is true."); } - result = new RemoteOperationResult(ResultCode.OK); + result = new RemoteOperationResult<>(ResultCode.OK); Log_OC.i(TAG, "Checked " + user.getAccountName() + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed")); @@ -407,12 +457,10 @@ public class RefreshFolderOperation extends RemoteOperation { return result; } - private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) { String remotePath = mLocalFolder.getRemotePath(); RemoteOperationResult result = new ReadFolderRemoteOperation(remotePath).execute(client); - Log_OC.d(TAG, "Refresh folder " + user.getAccountName() + remotePath); - Log_OC.d(TAG, "Refresh folder with remote id" + mLocalFolder.getRemoteId()); + Log_OC.d(TAG, "⬇ eTag is changed or ignored, fetching folder: " + user.getAccountName() + remotePath); if (result.isSuccess()) { synchronizeData(result.getData()); @@ -430,13 +478,13 @@ public class RefreshFolderOperation extends RemoteOperation { } private void removeLocalFolder() { - if (mStorageManager.fileExists(mLocalFolder.getFileId())) { + if (fileDataStorageManager.fileExists(mLocalFolder.getFileId())) { String currentSavePath = FileStorageUtils.getSavePath(user.getAccountName()); - mStorageManager.removeFolder( + fileDataStorageManager.removeFolder( mLocalFolder, true, mLocalFolder.isDown() && mLocalFolder.getStoragePath().startsWith(currentSavePath) - ); + ); } } @@ -451,7 +499,7 @@ public class RefreshFolderOperation extends RemoteOperation { */ private void synchronizeData(List folderAndFiles) { // get 'fresh data' from the database - mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath()); + mLocalFolder = fileDataStorageManager.getFileByPath(mLocalFolder.getRemotePath()); if (mLocalFolder == null) { Log_OC.d(TAG,"mLocalFolder cannot be null"); @@ -469,7 +517,7 @@ public class RefreshFolderOperation extends RemoteOperation { mFilesToSyncContents.clear(); // if local folder is encrypted, download fresh metadata - boolean encryptedAncestor = FileStorageUtils.checkEncryptionStatus(mLocalFolder, mStorageManager); + boolean encryptedAncestor = FileStorageUtils.checkEncryptionStatus(mLocalFolder, fileDataStorageManager); mLocalFolder.setEncrypted(encryptedAncestor); // update permission @@ -502,14 +550,12 @@ public class RefreshFolderOperation extends RemoteOperation { // get current data about local contents of the folder to synchronize Map localFilesMap; E2EVersion e2EVersion; - if (object instanceof DecryptedFolderMetadataFileV1) { + if (object instanceof DecryptedFolderMetadataFileV1 metadataFileV1) { e2EVersion = E2EVersion.V1_2; - localFilesMap = prefillLocalFilesMap((DecryptedFolderMetadataFileV1) object, - mStorageManager.getFolderContent(mLocalFolder, false)); + localFilesMap = prefillLocalFilesMap(metadataFileV1, fileDataStorageManager.getFolderContent(mLocalFolder, false)); } else { e2EVersion = E2EVersion.V2_0; - localFilesMap = prefillLocalFilesMap((DecryptedFolderMetadataFile) object, - mStorageManager.getFolderContent(mLocalFolder, false)); + localFilesMap = prefillLocalFilesMap(object, fileDataStorageManager.getFolderContent(mLocalFolder, false)); // update counter if (object != null) { @@ -537,7 +583,7 @@ public class RefreshFolderOperation extends RemoteOperation { // TODO better implementation is needed if (localFile == null) { - localFile = mStorageManager.getFileByPath(updatedFile.getRemotePath()); + localFile = fileDataStorageManager.getFileByPath(updatedFile.getRemotePath()); } // add to updatedFile data about LOCAL STATE (not existing in server) @@ -556,11 +602,11 @@ public class RefreshFolderOperation extends RemoteOperation { // update file name for encrypted files if (e2EVersion == E2EVersion.V1_2) { - updateFileNameForEncryptedFileV1(mStorageManager, + updateFileNameForEncryptedFileV1(fileDataStorageManager, (DecryptedFolderMetadataFileV1) object, updatedFile); - } else { - updateFileNameForEncryptedFile(mStorageManager, + } else if (object != null) { + updateFileNameForEncryptedFile(fileDataStorageManager, (DecryptedFolderMetadataFile) object, updatedFile); if (localFile != null) { @@ -579,15 +625,15 @@ public class RefreshFolderOperation extends RemoteOperation { // save updated contents in local database // update file name for encrypted files if (e2EVersion == E2EVersion.V1_2) { - updateFileNameForEncryptedFileV1(mStorageManager, + updateFileNameForEncryptedFileV1(fileDataStorageManager, (DecryptedFolderMetadataFileV1) object, mLocalFolder); } else { - updateFileNameForEncryptedFile(mStorageManager, + updateFileNameForEncryptedFile(fileDataStorageManager, (DecryptedFolderMetadataFile) object, mLocalFolder); } - mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values()); + fileDataStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values()); mChildren = updatedFiles; } @@ -697,6 +743,7 @@ public class RefreshFolderOperation extends RemoteOperation { if (localFile != null) { updatedFile.setFileId(localFile.getFileId()); updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData()); + updatedFile.setInternalFolderSyncTimestamp(localFile.getInternalFolderSyncTimestamp()); updatedFile.setModificationTimestampAtLastSyncForData( localFile.getModificationTimestampAtLastSyncForData() ); @@ -790,38 +837,6 @@ public class RefreshFolderOperation extends RemoteOperation { } } - /** - * Syncs the Share resources for the files contained in the folder refreshed (children, not deeper descendants). - * - * @param client Handler of a session with an OC server. - * @return The result of the remote operation retrieving the Share resources in the folder refreshed by the - * operation. - */ - private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { - RemoteOperationResult result; - - // remote request - GetSharesForFileRemoteOperation operation = - new GetSharesForFileRemoteOperation(mLocalFolder.getRemotePath(), true, true); - result = operation.execute(client); - - if (result.isSuccess()) { - // update local database - ArrayList shares = new ArrayList<>(); - OCShare share; - for (Object obj : result.getData()) { - share = (OCShare) obj; - - if (ShareType.NO_SHARED != share.getShareType()) { - shares.add(share); - } - } - mStorageManager.saveSharesInFolder(shares, mLocalFolder); - } - - return result; - } - /** * Sends a message to any application component interested in the progress of the synchronization. * diff --git a/app/src/main/java/com/owncloud/android/operations/RemoteOperationFailedException.java b/app/src/main/java/com/owncloud/android/operations/RemoteOperationFailedException.java index d52e798..c797105 100644 --- a/app/src/main/java/com/owncloud/android/operations/RemoteOperationFailedException.java +++ b/app/src/main/java/com/owncloud/android/operations/RemoteOperationFailedException.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017-2019 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java b/app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java deleted file mode 100644 index ab3239b..0000000 --- a/app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2015 ownCloud Inc. - * SPDX-FileCopyrightText: 2015 María Asensio Valverde - * SPDX-FileCopyrightText: 2012 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later - */ -package com.owncloud.android.operations; - -import android.content.Context; - -import com.nextcloud.client.account.User; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation; -import com.owncloud.android.operations.common.SyncOperation; -import com.owncloud.android.utils.MimeTypeUtil; - -/** - * Remote operation performing the removal of a remote file or folder in the ownCloud server. - */ -public class RemoveFileOperation extends SyncOperation { - - private final OCFile fileToRemove; - private final boolean onlyLocalCopy; - private final User user; - private final boolean inBackground; - private final Context context; - - /** - * Constructor - * - * @param fileToRemove OCFile instance describing the remote file or folder to remove from the server - * @param onlyLocalCopy When 'true', and a local copy of the file exists, only this is removed. - */ - public RemoveFileOperation(OCFile fileToRemove, - boolean onlyLocalCopy, - User user, - boolean inBackground, - Context context, - FileDataStorageManager storageManager) { - super(storageManager); - - this.fileToRemove = fileToRemove; - this.onlyLocalCopy = onlyLocalCopy; - this.user = user; - this.inBackground = inBackground; - this.context = context; - } - - /** - * Getter for the file to remove (or removed, if the operation was successfully performed). - * - * @return File to remove or already removed. - */ - public OCFile getFile() { - return fileToRemove; - } - - public boolean isInBackground() { - return inBackground; - } - - /** - * Performs the remove operation - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = null; - RemoteOperation operation; - - if (MimeTypeUtil.isImage(fileToRemove.getMimeType())) { - // store resized image - ThumbnailsCacheManager.generateResizedImage(fileToRemove); - } - - boolean localRemovalFailed = false; - if (!onlyLocalCopy) { - if (fileToRemove.isEncrypted()) { - OCFile parent = getStorageManager().getFileByPath(fileToRemove.getParentRemotePath()); - operation = new RemoveRemoteEncryptedFileOperation(fileToRemove.getRemotePath(), - user, - context, - fileToRemove.getEncryptedFileName(), - parent, - fileToRemove.isFolder()); - } else { - operation = new RemoveFileRemoteOperation(fileToRemove.getRemotePath()); - } - result = operation.execute(client); - if (result.isSuccess() || result.getCode() == ResultCode.FILE_NOT_FOUND) { - localRemovalFailed = !(getStorageManager().removeFile(fileToRemove, true, true)); - } - } else { - localRemovalFailed = !(getStorageManager().removeFile(fileToRemove, false, true)); - if (!localRemovalFailed) { - result = new RemoteOperationResult(ResultCode.OK); - } - } - - if (localRemovalFailed) { - result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_REMOVED); - } - - return result; - } -} diff --git a/app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.kt new file mode 100644 index 0000000..d1fd55d --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/RemoveFileOperation.kt @@ -0,0 +1,99 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2015 ownCloud Inc. + * SPDX-FileCopyrightText: 2015 María Asensio Valverde + * SPDX-FileCopyrightText: 2012 David A. Velasco + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + */ +package com.owncloud.android.operations +import android.content.Context +import com.nextcloud.client.account.User +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.ThumbnailsCacheManager +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation +import com.owncloud.android.operations.common.SyncOperation +import com.owncloud.android.utils.MimeTypeUtil + +/** + * Remote operation to remove a remote file or folder from an ownCloud server. + * + * @param file OCFile instance representing the remote file or folder to remove. + * @param onlyLocalCopy If true, only the local copy will be removed (if it exists). + * @param user User account associated with the operation. + * @param isInBackground Flag indicating if the operation runs in the background. + * @param context Android context. + * @param storageManager Storage manager handling local file operations. + */ +@Suppress("LongParameterList") +class RemoveFileOperation( + val file: OCFile, + private val onlyLocalCopy: Boolean, + private val user: User, + val isInBackground: Boolean, + private val context: Context, + storageManager: FileDataStorageManager +) : SyncOperation(storageManager) { + + /** + * Executes the remove operation. + * + * If the file is an image, it will also be removed from the thumbnail cache. + * Handles both encrypted and non-encrypted files. Removes the file locally if needed. + * + * @param client OwnCloudClient used to communicate with the remote server. + * @return RemoteOperationResult indicating success or failure of the operation. + */ + override fun run(client: OwnCloudClient?): RemoteOperationResult<*> { + var result: RemoteOperationResult<*>? = null + val operation: RemoteOperation<*>? + + var localRemovalFailed = false + + if (onlyLocalCopy) { + // generate resize image if image is deleted only locally, to save server request + if (MimeTypeUtil.isImage(file.mimeType)) { + ThumbnailsCacheManager.generateResizedImage(file) + } + + localRemovalFailed = !storageManager.removeFile(file, false, true) + if (!localRemovalFailed) { + result = RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + } + } else { + operation = if (file.isEncrypted) { + val parent = storageManager.getFileById(file.parentId) + if (parent == null) { + return RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_FILE_NOT_FOUND) + } + RemoveRemoteEncryptedFileOperation( + file.remotePath, + user, + context, + file.getEncryptedFileName(), + parent, + file.isFolder + ) + } else { + RemoveFileRemoteOperation(file.remotePath) + } + + result = operation.execute(client) + if (result.isSuccess || result.code == RemoteOperationResult.ResultCode.FILE_NOT_FOUND) { + localRemovalFailed = !storageManager.removeFile(file, true, true) + } + } + + if (localRemovalFailed) { + result = RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_REMOVED) + } + + return result ?: RemoteOperationResult(RemoteOperationResult.ResultCode.CANCELLED) + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt index 6248d1a..5c85d0a 100644 --- a/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations @@ -52,7 +52,7 @@ class RemoveRemoteEncryptedFileOperation internal constructor( @Deprecated("Deprecated in Java") @Suppress("TooGenericExceptionCaught") override fun run(client: OwnCloudClient): RemoteOperationResult { - val result: RemoteOperationResult + var result: RemoteOperationResult var delete: DeleteMethod? = null var token: String? = null val e2eVersion = CapabilityUtils.getCapability(context).endToEndEncryptionApiVersion diff --git a/app/src/main/java/com/owncloud/android/operations/RenameFileOperation.java b/app/src/main/java/com/owncloud/android/operations/RenameFileOperation.java index 2fb1b9a..3f1c3a4 100644 --- a/app/src/main/java/com/owncloud/android/operations/RenameFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RenameFileOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2012 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/RichDocumentsCreateAssetOperation.java b/app/src/main/java/com/owncloud/android/operations/RichDocumentsCreateAssetOperation.java index 2fe795a..e593a20 100644 --- a/app/src/main/java/com/owncloud/android/operations/RichDocumentsCreateAssetOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RichDocumentsCreateAssetOperation.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/RichDocumentsUrlOperation.java b/app/src/main/java/com/owncloud/android/operations/RichDocumentsUrlOperation.java index a236948..a05a892 100644 --- a/app/src/main/java/com/owncloud/android/operations/RichDocumentsUrlOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RichDocumentsUrlOperation.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt new file mode 100644 index 0000000..8918172 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt @@ -0,0 +1,55 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+zetatom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations + +import android.content.Context +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.nextcloud.android.lib.resources.files.RemoveFilesDownloadLimitRemoteOperation +import com.nextcloud.android.lib.resources.files.SetFilesDownloadLimitRemoteOperation +import com.nextcloud.utils.extensions.toNextcloudClient +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult + +class SetFilesDownloadLimitOperation( + private val shareId: Long, + private val newLimit: Int, + private val fileDataStorageManager: FileDataStorageManager, + private val context: Context +) : RemoteOperation() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + val nextcloudClient = client.toNextcloudClient(context) + val share = fileDataStorageManager.getShareById(shareId) + val token = share?.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) + + val result = if (newLimit > 0) { + val operation = SetFilesDownloadLimitRemoteOperation(token, newLimit) + nextcloudClient.execute(operation) + } else { + val operation = RemoveFilesDownloadLimitRemoteOperation(token) + nextcloudClient.execute(operation) + } + + val path = share.path + if (result.isSuccess && path != null) { + val getFilesDownloadLimitRemoteOperation = GetFilesDownloadLimitRemoteOperation(path, false) + val remoteOperationResult = getFilesDownloadLimitRemoteOperation.execute(client) + + if (remoteOperationResult.isSuccess) { + share.fileDownloadLimit = remoteOperationResult.resultData.firstOrNull { updatedDownloadLimit -> + updatedDownloadLimit.token == share.token + } + fileDataStorageManager.saveShare(share) + } + } + + return result + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index b141136..4aba8f2 100644 --- a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2021 Chris Narkiewicz * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2016-2018 Andy Scherzinger @@ -9,7 +9,7 @@ * SPDX-FileCopyrightText: 2013-2016 María Asensio Valverde * SPDX-FileCopyrightText: 2012 David A. Velasco * SPDX-FileCopyrightText: 2012 Bartek Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -30,8 +30,12 @@ import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.ui.events.DialogEvent; +import com.owncloud.android.ui.events.DialogEventType; import com.owncloud.android.utils.FileStorageUtils; +import org.greenrobot.eventbus.EventBus; + /** * Remote operation performing the read of remote file in the ownCloud server. */ @@ -46,6 +50,9 @@ public class SynchronizeFileOperation extends SyncOperation { private boolean mSyncFileContents; private Context mContext; private boolean mTransferWasRequested; + private final boolean syncInBackgroundWorker; + private boolean postDialogEvent = true; + /** * When 'false', uploads to the server are not done; only downloads or conflict detection. This is a temporal @@ -74,7 +81,9 @@ public class SynchronizeFileOperation extends SyncOperation { User user, boolean syncFileContents, Context context, - FileDataStorageManager storageManager) { + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker, + boolean postDialogEvent) { super(storageManager); mRemotePath = remotePath; @@ -84,6 +93,8 @@ public class SynchronizeFileOperation extends SyncOperation { mSyncFileContents = syncFileContents; mContext = context; mAllowUploads = true; + this.syncInBackgroundWorker = syncInBackgroundWorker; + this.postDialogEvent = postDialogEvent; } @@ -110,7 +121,8 @@ public class SynchronizeFileOperation extends SyncOperation { User user, boolean syncFileContents, Context context, - FileDataStorageManager storageManager) { + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker) { super(storageManager); mLocalFile = localFile; @@ -130,6 +142,7 @@ public class SynchronizeFileOperation extends SyncOperation { mSyncFileContents = syncFileContents; mContext = context; mAllowUploads = true; + this.syncInBackgroundWorker = syncInBackgroundWorker; } @@ -159,9 +172,9 @@ public class SynchronizeFileOperation extends SyncOperation { boolean syncFileContents, boolean allowUploads, Context context, - FileDataStorageManager storageManager) { - - this(localFile, serverFile, user, syncFileContents, context, storageManager); + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker) { + this(localFile, serverFile, user, syncFileContents, context, storageManager, syncInBackgroundWorker); mAllowUploads = allowUploads; } @@ -275,6 +288,9 @@ public class SynchronizeFileOperation extends SyncOperation { Log_OC.i(TAG, "Synchronizing " + mUser.getAccountName() + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage()); + if (postDialogEvent) { + EventBus.getDefault().post(new DialogEvent(DialogEventType.SYNC)); + } return result; } @@ -295,11 +311,32 @@ public class SynchronizeFileOperation extends SyncOperation { } private void requestForDownload(OCFile file) { - FileDownloadHelper.Companion.instance().downloadFile( - mUser, - file); + final var fileDownloadHelper = FileDownloadHelper.Companion.instance(); + final var filename = file.getFileName(); - mTransferWasRequested = true; + if (syncInBackgroundWorker) { + Log_OC.d(TAG, "downloading file without notification: " + filename); + + try { + final var operation = new DownloadFileOperation(mUser, file, mContext); + final var result = operation.execute(getClient()); + + mTransferWasRequested = true; + + if (result.isSuccess()) { + fileDownloadHelper.saveFile(file, operation, getStorageManager()); + Log_OC.d(TAG, "requestForDownload completed for: " + filename); + } else { + Log_OC.d(TAG, "requestForDownload failed for: " + filename); + } + } catch (Exception e) { + Log_OC.d(TAG, "Exception caught at requestForDownload" + e); + } + } else { + Log_OC.d(TAG, "downloading file with notification: " + filename); + mTransferWasRequested = true; + fileDownloadHelper.downloadFile(mUser, file); + } } public boolean transferWasRequested() { diff --git a/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index f944977..20a75f5 100644 --- a/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2018 Andy Scherzinger * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2012-2013 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -55,9 +55,6 @@ public class SynchronizeFolderOperation extends SyncOperation { private static final String TAG = SynchronizeFolderOperation.class.getSimpleName(); - /** Time stamp for the synchronization process in progress */ - private long mCurrentSyncTime; - /** Remote path of the folder to synchronize */ private String mRemotePath; @@ -89,29 +86,30 @@ public class SynchronizeFolderOperation extends SyncOperation { private final AtomicBoolean mCancellationRequested; + private final boolean syncInBackgroundWorker; + /** * Creates a new instance of {@link SynchronizeFolderOperation}. * * @param context Application context. * @param remotePath Path to synchronize. * @param user Nextcloud account where the folder is located. - * @param currentSyncTime Time stamp for the synchronization process in progress. */ public SynchronizeFolderOperation(Context context, String remotePath, User user, - long currentSyncTime, - FileDataStorageManager storageManager) { + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker) { super(storageManager); mRemotePath = remotePath; - mCurrentSyncTime = currentSyncTime; this.user = user; mContext = context; mRemoteFolderChanged = false; mFilesForDirectDownload = new Vector<>(); mFilesToSyncContents = new Vector<>(); mCancellationRequested = new AtomicBoolean(false); + this.syncInBackgroundWorker = syncInBackgroundWorker; } @@ -135,13 +133,12 @@ public class SynchronizeFolderOperation extends SyncOperation { if (result.isSuccess()) { if (mRemoteFolderChanged) { result = fetchAndSyncRemoteFolder(client); - } else { prepareOpsFromLocalKnowledge(); } if (result.isSuccess()) { - syncContents(); + syncContents(client); } } @@ -283,18 +280,7 @@ public class SynchronizeFolderOperation extends SyncOperation { } // get current data about local contents of the folder to synchronize - Map localFilesMap; - E2EVersion e2EVersion; - - if (object instanceof DecryptedFolderMetadataFileV1) { - e2EVersion = E2EVersion.V1_2; - localFilesMap = RefreshFolderOperation.prefillLocalFilesMap((DecryptedFolderMetadataFileV1) object, - storageManager.getFolderContent(mLocalFolder, false)); - } else { - e2EVersion = E2EVersion.V2_0; - localFilesMap = RefreshFolderOperation.prefillLocalFilesMap((DecryptedFolderMetadataFile) object, - storageManager.getFolderContent(mLocalFolder, false)); - } + Map localFilesMap = RefreshFolderOperation.prefillLocalFilesMap(object,storageManager.getFolderContent(mLocalFolder, false)); // loop to synchronize every child List updatedFiles = new ArrayList<>(folderAndFiles.size() - 1); @@ -327,14 +313,10 @@ public class SynchronizeFolderOperation extends SyncOperation { FileStorageUtils.searchForLocalFileInDefaultPath(updatedFile, user.getAccountName()); // update file name for encrypted files - if (e2EVersion == E2EVersion.V1_2) { - RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, - (DecryptedFolderMetadataFileV1) object, - updatedFile); - } else { - RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, - (DecryptedFolderMetadataFile) object, - updatedFile); + if (object instanceof DecryptedFolderMetadataFileV1 metadataFile) { + RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, metadataFile, updatedFile); + } else if (object instanceof DecryptedFolderMetadataFile metadataFile) { + RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadataFile, updatedFile); } // we parse content, so either the folder itself or its direct parent (which we check) must be encrypted @@ -348,14 +330,10 @@ public class SynchronizeFolderOperation extends SyncOperation { } // update file name for encrypted files - if (e2EVersion == E2EVersion.V1_2) { - RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, - (DecryptedFolderMetadataFileV1) object, - mLocalFolder); - } else { - RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, - (DecryptedFolderMetadataFile) object, - mLocalFolder); + if (object instanceof DecryptedFolderMetadataFileV1 metadataFile) { + RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, metadataFile, mLocalFolder); + } else if (object instanceof DecryptedFolderMetadataFile metadataFile) { + RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadataFile, mLocalFolder); } // save updated contents in local database @@ -365,7 +343,7 @@ public class SynchronizeFolderOperation extends SyncOperation { } private void updateLocalStateData(OCFile remoteFile, OCFile localFile, OCFile updatedFile) { - updatedFile.setLastSyncDateForProperties(mCurrentSyncTime); + updatedFile.setLastSyncDateForProperties(System.currentTimeMillis()); if (localFile != null) { updatedFile.setFileId(localFile.getFileId()); updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData()); @@ -393,15 +371,27 @@ public class SynchronizeFolderOperation extends SyncOperation { } } - private void classifyFileForLaterSyncOrDownload(OCFile remoteFile, OCFile localFile) { - if (!remoteFile.isFolder()) { + @SuppressFBWarnings("JLM") + private void classifyFileForLaterSyncOrDownload(OCFile remoteFile, OCFile localFile) throws OperationCancelledException { + if (remoteFile.isFolder()) { + /// to download children files recursively + synchronized (mCancellationRequested) { + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } + startSyncFolderOperation(remoteFile.getRemotePath()); + } + + } else { + /// prepare content synchronization for files (any file, not just favorites) SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile, remoteFile, user, true, mContext, - getStorageManager() + getStorageManager(), + syncInBackgroundWorker ); mFilesToSyncContents.add(operation); } @@ -414,7 +404,6 @@ public class SynchronizeFolderOperation extends SyncOperation { if (!child.isFolder()) { if (!child.isDown()) { mFilesForDirectDownload.add(child); - } else { /// this should result in direct upload of files that were locally modified SynchronizeFileOperation operation = new SynchronizeFileOperation( @@ -423,23 +412,81 @@ public class SynchronizeFolderOperation extends SyncOperation { user, true, mContext, - getStorageManager() + getStorageManager(), + syncInBackgroundWorker ); mFilesToSyncContents.add(operation); - } } } } - private void syncContents() throws OperationCancelledException { + private void syncContents(OwnCloudClient client) throws OperationCancelledException { startDirectDownloads(); startContentSynchronizations(mFilesToSyncContents); + updateETag(client); } + /** + * Updates the eTag of the local folder after a successful synchronization. + * This ensures that any changes to local files, which may alter the eTag, are correctly reflected. + * + * @param client the OwnCloudClient instance used to execute remote operations. + */ + private void updateETag(OwnCloudClient client) { + ReadFolderRemoteOperation operation = new ReadFolderRemoteOperation(mRemotePath); + final var result = operation.execute(client); + if (!result.isSuccess()) { + Log_OC.w(TAG, "Cannot update eTag, read folder operation is failed"); + return; + } + + if (result.getData().get(0) instanceof RemoteFile remoteFile) { + String eTag = remoteFile.getEtag(); + mLocalFolder.setEtag(eTag); + + final FileDataStorageManager storageManager = getStorageManager(); + storageManager.saveFile(mLocalFolder); + } + } private void startDirectDownloads() { - FileDownloadHelper.Companion.instance().downloadFile(user, mLocalFolder); + final var fileDownloadHelper = FileDownloadHelper.Companion.instance(); + + if (syncInBackgroundWorker) { + try { + for (OCFile file: mFilesForDirectDownload) { + synchronized (mCancellationRequested) { + if (mCancellationRequested.get()) { + break; + } + } + + if (file == null) { + continue; + } + + final var operation = new DownloadFileOperation(user, file, mContext); + var result = operation.execute(getClient()); + + String filename = file.getFileName(); + if (filename == null) { + continue; + } + + if (result.isSuccess()) { + fileDownloadHelper.saveFile(file, operation, getStorageManager()); + Log_OC.d(TAG, "startDirectDownloads completed for: " + file.getFileName()); + } else { + Log_OC.d(TAG, "startDirectDownloads failed for: " + file.getFileName()); + } + } + } catch (Exception e) { + Log_OC.d(TAG, "Exception caught at startDirectDownloads" + e); + } + } else { + mFilesForDirectDownload.forEach(file -> fileDownloadHelper.downloadFile(user, file)); + } } /** diff --git a/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java b/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java index d45d8b6..3735703 100644 --- a/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UnshareOperation.java @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2018 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2014 María Asensio Valverde - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -113,6 +113,8 @@ public class UnshareOperation extends SyncOperation { RemoveShareRemoteOperation operation = new RemoveShareRemoteOperation(share.getRemoteId()); result = operation.execute(client); + boolean isFileExists = existsFile(client, file.getRemotePath()); + boolean isShareExists = getStorageManager().getShareById(shareId) != null; if (result.isSuccess()) { // E2E: unlock folder @@ -128,7 +130,7 @@ public class UnshareOperation extends SyncOperation { if (ShareType.PUBLIC_LINK == share.getShareType()) { file.setSharedViaLink(false); } else if (ShareType.USER == share.getShareType() || ShareType.GROUP == share.getShareType() - || ShareType.FEDERATED == share.getShareType()) { + || ShareType.FEDERATED == share.getShareType() || ShareType.FEDERATED_GROUP == share.getShareType()) { // Check if it is the last share List sharesWith = getStorageManager(). getSharesWithForAFile(remotePath, @@ -140,10 +142,12 @@ public class UnshareOperation extends SyncOperation { getStorageManager().saveFile(file); getStorageManager().removeShare(share); - - } else if (result.getCode() != ResultCode.MAINTENANCE_MODE && !existsFile(client, file.getRemotePath())) { - // unshare failed because file was deleted before + } else if (result.getCode() != ResultCode.MAINTENANCE_MODE && !isFileExists) { + // UnShare failed because file was deleted before getStorageManager().removeFile(file, true, true); + } else if (isShareExists && result.getCode() == ResultCode.FILE_NOT_FOUND) { + // UnShare failed because share was deleted before + getStorageManager().removeShare(share); } } else { diff --git a/app/src/main/java/com/owncloud/android/operations/UpdateNoteForShareOperation.java b/app/src/main/java/com/owncloud/android/operations/UpdateNoteForShareOperation.java index 972b7ec..d10b5e3 100644 --- a/app/src/main/java/com/owncloud/android/operations/UpdateNoteForShareOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UpdateNoteForShareOperation.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/UpdateOCVersionOperation.java b/app/src/main/java/com/owncloud/android/operations/UpdateOCVersionOperation.java index abd4ac1..e8f9fc1 100644 --- a/app/src/main/java/com/owncloud/android/operations/UpdateOCVersionOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UpdateOCVersionOperation.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2017 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2012 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/UpdateShareInfoOperation.java b/app/src/main/java/com/owncloud/android/operations/UpdateShareInfoOperation.java index e69a426..3d21989 100644 --- a/app/src/main/java/com/owncloud/android/operations/UpdateShareInfoOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UpdateShareInfoOperation.java @@ -5,7 +5,7 @@ * Copyright (C) 2021 TSI-mc * Copyright (C) 2021 Nextcloud GmbH * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; @@ -16,6 +16,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.GetShareRemoteOperation; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.UpdateShareRemoteOperation; @@ -29,12 +30,16 @@ public class UpdateShareInfoOperation extends SyncOperation { private OCShare share; private long shareId; + private long shareRemoteId; private long expirationDateInMillis; private String note; private boolean hideFileDownload; private int permissions = -1; private String password; private String label; + private String attributes; + + private static final String TAG = "UpdateShareInfoOperation"; /** * Constructor @@ -58,9 +63,10 @@ public class UpdateShareInfoOperation extends SyncOperation { *

    * this will be triggered while modifying existing share */ - public UpdateShareInfoOperation(long shareId, FileDataStorageManager storageManager) { + public UpdateShareInfoOperation(long shareId, long shareRemoteId, FileDataStorageManager storageManager) { super(storageManager); - + + this.shareRemoteId = shareRemoteId; this.shareId = shareId; expirationDateInMillis = 0L; note = null; @@ -76,9 +82,18 @@ public class UpdateShareInfoOperation extends SyncOperation { share = this.share; } + if (share == null && shareRemoteId > 0) { + Log_OC.w(TAG,"share is null, trying to fetch"); + final var shareRemoteOperation = new GetShareRemoteOperation(shareRemoteId); + final var result = shareRemoteOperation.execute(client); + if (result.isSuccess()) { + share = (OCShare) result.getData().get(0); + } + } + if (share == null) { - // TODO try to get remote share before failing? - return new RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND); + Log_OC.e(TAG,"share is null, fetching operation is failed"); + return new RemoteOperationResult<>(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND); } // Update remote share @@ -93,11 +108,12 @@ public class UpdateShareInfoOperation extends SyncOperation { } updateOp.setPassword(password); updateOp.setLabel(label); + updateOp.setAttributes(attributes); - RemoteOperationResult result = updateOp.execute(client); + var result = updateOp.execute(client); if (result.isSuccess()) { - RemoteOperation getShareOp = new GetShareRemoteOperation(share.getRemoteId()); + final var getShareOp = new GetShareRemoteOperation(share.getRemoteId()); result = getShareOp.execute(client); //only update the share in storage if shareId is available @@ -105,9 +121,10 @@ public class UpdateShareInfoOperation extends SyncOperation { if (result.isSuccess() && shareId > 0) { OCShare ocShare = (OCShare) result.getData().get(0); ocShare.setPasswordProtected(!TextUtils.isEmpty(password)); + ocShare.setRemoteId(shareRemoteId); + ocShare.setId(shareId); getStorageManager().saveShare(ocShare); } - } return result; @@ -125,6 +142,10 @@ public class UpdateShareInfoOperation extends SyncOperation { this.hideFileDownload = hideFileDownload; } + public void setAttributes(String attributes) { + this.attributes = attributes; + } + public void setPermissions(int permissions) { this.permissions = permissions; } diff --git a/app/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java b/app/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java index 3e1eb53..aef7479 100644 --- a/app/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2018 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; @@ -49,7 +49,7 @@ public class UpdateSharePermissionsOperation extends SyncOperation { @Override protected RemoteOperationResult run(OwnCloudClient client) { - OCShare share = getStorageManager().getShareById(shareId); // ShareType.USER | ShareType.GROUP + OCShare share = getStorageManager().getShareById(shareId); // ShareType.USER | ShareType.GROUP | ShareType.FEDERATED_GROUP if (share == null) { // TODO try to get remote share before failing? diff --git a/app/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java b/app/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java index a7c06ea..834c08a 100644 --- a/app/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2020-2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/UploadException.java b/app/src/main/java/com/owncloud/android/operations/UploadException.java index d29db71..080fb50 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadException.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadException.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 88101e6..29db48f 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -6,22 +6,23 @@ * SPDX-FileCopyrightText: 2017-2018 Andy Scherzinger * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2012 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations; import android.annotation.SuppressLint; import android.content.Context; -import android.content.Intent; import android.net.Uri; import android.text.TextUtils; import com.nextcloud.client.account.User; import com.nextcloud.client.device.BatteryStatus; import com.nextcloud.client.device.PowerManagementService; +import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.network.Connectivity; import com.nextcloud.client.network.ConnectivityService; +import com.nextcloud.utils.autoRename.AutoRename; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -51,7 +52,13 @@ import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.lib.resources.status.E2EVersion; +import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.operations.e2e.E2EClientData; +import com.owncloud.android.operations.e2e.E2EData; +import com.owncloud.android.operations.e2e.E2EFiles; +import com.owncloud.android.operations.upload.UploadFileException; +import com.owncloud.android.operations.upload.UploadFileOperationExtensionsKt; import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.EncryptionUtilsV2; import com.owncloud.android.utils.FileStorageUtils; @@ -77,20 +84,28 @@ import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.spec.InvalidParameterSpecException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import javax.crypto.BadPaddingException; import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import androidx.annotation.CheckResult; import androidx.annotation.Nullable; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - -import static com.owncloud.android.ui.activity.FileDisplayActivity.REFRESH_FOLDER_EVENT_RECEIVER; +import kotlin.Triple; +import kotlin.Unit; /** * Operation performing the update in the ownCloud server of a file that was modified locally. @@ -102,6 +117,7 @@ public class UploadFileOperation extends SyncOperation { public static final int CREATED_BY_USER = 0; public static final int CREATED_AS_INSTANT_PICTURE = 1; public static final int CREATED_AS_INSTANT_VIDEO = 2; + public static final int MISSING_FILE_PERMISSION_NOTIFICATION_ID = 2501; /** * OCFile which is to be uploaded. @@ -149,6 +165,8 @@ public class UploadFileOperation extends SyncOperation { private final PowerManagementService powerManagementService; private boolean encryptedAncestor; + private OCFile duplicatedEncryptedFile; + private AtomicBoolean missingPermissionThrown = new AtomicBoolean(false); public static OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) { OCFile newFile = new OCFile(remotePath); @@ -217,9 +235,11 @@ public class UploadFileOperation extends SyncOperation { super(storageManager); if (upload == null) { + Log_OC.e(TAG, "UploadFileOperation upload is null cant construct"); throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation"); } if (TextUtils.isEmpty(upload.getLocalPath())) { + Log_OC.e(TAG, "UploadFileOperation local path is null cant construct"); throw new IllegalArgumentException( "Illegal file in UploadFileOperation; storage path invalid: " + upload.getLocalPath()); @@ -231,11 +251,11 @@ public class UploadFileOperation extends SyncOperation { this.user = user; mUpload = upload; if (file == null) { + Log_OC.w(TAG, "UploadFileOperation file is null, obtaining from upload"); mFile = obtainNewOCFileToUpload( upload.getRemotePath(), upload.getLocalPath(), - upload.getMimeType() - ); + upload.getMimeType()); } else { mFile = file; } @@ -386,40 +406,59 @@ public class UploadFileOperation extends SyncOperation { return mContext; } + public boolean isMissingPermissionThrown() { + return missingPermissionThrown.get(); + } + @Override @SuppressWarnings("PMD.AvoidDuplicateLiterals") protected RemoteOperationResult run(OwnCloudClient client) { + if (TextUtils.isEmpty(getStoragePath())) { + Log_OC.e(TAG, "Upload cancelled for " + getStoragePath() + ": file path is null or empty."); + return new RemoteOperationResult<>(new UploadFileException.EmptyOrNullFilePath()); + } + + final var localFile = new File(getStoragePath()); + if (!localFile.exists()) { + Log_OC.e(TAG, "Upload cancelled for " + getStoragePath() + ": local file not exists."); + return new RemoteOperationResult<>(ResultCode.LOCAL_FILE_NOT_FOUND); + } + + if (!localFile.canRead()) { + Log_OC.e(TAG, "Upload cancelled for " + getStoragePath() + ": file is not readable or inaccessible."); + UploadFileOperationExtensionsKt.showStoragePermissionNotification(this); + missingPermissionThrown.set(true); + return new RemoteOperationResult<>(new UploadFileException.MissingPermission()); + } + mCancellationRequested.set(false); mUploadStarted.set(true); updateSize(0); String remoteParentPath = new File(getRemotePath()).getParent(); - remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? - remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR; + remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR; + remoteParentPath = AutoRename.INSTANCE.rename(remoteParentPath, getCapabilities()); OCFile parent = getStorageManager().getFileByPath(remoteParentPath); // in case of a fresh upload with subfolder, where parent does not exist yet if (parent == null && (mFolderUnlockToken == null || mFolderUnlockToken.isEmpty())) { // try to create folder - RemoteOperationResult result = grantFolderExistence(remoteParentPath, client); + final var result = grantFolderExistence(remoteParentPath, client); if (!result.isSuccess()) { return result; } parent = getStorageManager().getFileByPath(remoteParentPath); - - if (parent == null) { - return new RemoteOperationResult(false, "Parent folder not found", HttpStatus.SC_NOT_FOUND); - } } - // parent file is not null anymore: - // - it was created on fresh upload or - // - resume of encrypted upload, then parent file exists already as unlock is only for direct parent + if (parent == null) { + return new RemoteOperationResult<>(false, "Parent folder not found", HttpStatus.SC_NOT_FOUND); + } + // - resume of encrypted upload, then parent file exists already as unlock is only for direct parent mFile.setParentId(parent.getFileId()); // check if any parent is encrypted @@ -435,14 +474,11 @@ public class UploadFileOperation extends SyncOperation { } } - // TODO REFACTOR + // region E2E Upload @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { RemoteOperationResult result = null; - File temporalFile = null; - File originalFile = new File(mOriginalStoragePath); - File expectedFile = null; - File encryptedTempFile = null; + E2EFiles e2eFiles = new E2EFiles(parentFile, null, new File(mOriginalStoragePath), null, null); FileLock fileLock = null; long size; @@ -454,29 +490,14 @@ public class UploadFileOperation extends SyncOperation { String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); try { - // check conditions - result = checkConditions(originalFile); + result = checkConditions(e2eFiles.getOriginalFile()); if (result != null) { return result; } - /***** E2E *****/ - // Only on V2+: whenever we change something, increase counter - long counter = -1; - if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { - counter = parentFile.getE2eCounter() + 1; - } - - // we might have an old token from interrupted upload - if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { - token = mFolderUnlockToken; - } else { - token = EncryptionUtils.lockFolder(parentFile, client, counter); - // immediately store it - mUpload.setFolderUnlockToken(token); - uploadsStorageManager.updateUpload(mUpload); - } + long counter = getE2ECounter(parentFile); + token = getFolderUnlockTokenOrLockFolder(client, parentFile, counter); // Update metadata EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); @@ -485,301 +506,427 @@ public class UploadFileOperation extends SyncOperation { metadataExists = true; } - if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { + if (isEndToEndVersionAtLeastV2()) { if (object == null) { - // TODO return error return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); } } else { - // v1 is allowed to be null, thus create it - DecryptedFolderMetadataFileV1 metadata = new DecryptedFolderMetadataFileV1(); - metadata.setMetadata(new DecryptedMetadata()); - metadata.getMetadata().setVersion(1.2); - metadata.getMetadata().setMetadataKeys(new HashMap<>()); - String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey()); - String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey); - metadata.getMetadata().setMetadataKey(encryptedMetadataKey); - - if (object instanceof DecryptedFolderMetadataFileV1) { - metadata = (DecryptedFolderMetadataFileV1) object; - } - - object = metadata; + object = getDecryptedFolderMetadataV1(publicKey, object); } - // todo fail if no metadata + E2EClientData clientData = new E2EClientData(client, token, publicKey); -// metadataExists = metadataPair.getFirst(); -// DecryptedFolderMetadataFile metadata = metadataPair.getSecond(); + List fileNames = getCollidedFileNames(object); - // TODO E2E: check counter: must be less than our counter, check rest: signature, etc - /**** E2E *****/ - - // check name collision - List fileNames = new ArrayList<>(); - if (object instanceof DecryptedFolderMetadataFileV1 metadata) { - for (DecryptedFile file : metadata.getFiles().values()) { - fileNames.add(file.getEncrypted().getFilename()); - } - } else { - for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : - ((DecryptedFolderMetadataFile) object).getMetadata().getFiles().values()) { - fileNames.add(file.getFilename()); - } - } - - RemoteOperationResult collisionResult = checkNameCollision(client, fileNames, parentFile.isEncrypted()); + RemoteOperationResult collisionResult = checkNameCollision(parentFile, client, fileNames, parentFile.isEncrypted()); if (collisionResult != null) { result = collisionResult; return collisionResult; } - mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + originalFile.getName()); + mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + e2eFiles.getOriginalFile().getName()); String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); - expectedFile = new File(expectedPath); + e2eFiles.setExpectedFile(new File(expectedPath)); - result = copyFile(originalFile, expectedPath); + result = copyFile(e2eFiles.getOriginalFile(), expectedPath); if (!result.isSuccess()) { return result; } - // Get the last modification date of the file from the file system - long lastModifiedTimestamp = originalFile.lastModified() / 1000; - - Long creationTimestamp = FileUtil.getCreationTimestamp(originalFile); - - /***** E2E *****/ - byte[] key = EncryptionUtils.generateKey(); - byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); - Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); - File file = new File(mFile.getStoragePath()); - EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher); - - // new random file name, check if it exists in metadata - String encryptedFileName = EncryptionUtils.generateUid(); - - if (object instanceof DecryptedFolderMetadataFileV1 metadata) { - while (metadata.getFiles().get(encryptedFileName) != null) { - encryptedFileName = EncryptionUtils.generateUid(); - } - } else { - while (((DecryptedFolderMetadataFile) object).getMetadata().getFiles().get(encryptedFileName) != null) { - encryptedFileName = EncryptionUtils.generateUid(); - } + long lastModifiedTimestamp = e2eFiles.getOriginalFile().lastModified() / 1000; + Long creationTimestamp = FileUtil.getCreationTimestamp(e2eFiles.getOriginalFile()); + if (creationTimestamp == null) { + Log_OC.e(TAG, "UploadFileOperation creationTimestamp cannot be null"); + throw new NullPointerException("creationTimestamp cannot be null"); } - encryptedTempFile = encryptedFile.getEncryptedFile(); - - FileChannel channel = null; - try { - channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel(); - fileLock = channel.tryLock(); - } catch (FileNotFoundException e) { - // this basically means that the file is on SD card - // try to copy file to temporary dir if it doesn't exist - String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + - mFile.getRemotePath(); - mFile.setStoragePath(temporalPath); - temporalFile = new File(temporalPath); - - Files.deleteIfExists(Paths.get(temporalPath)); - result = copy(originalFile, temporalFile); - - if (result.isSuccess()) { - if (temporalFile.length() == originalFile.length()) { - channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel(); - fileLock = channel.tryLock(); - } else { - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); - } - } + E2EData e2eData = getE2EData(object); + e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); + if (e2eFiles.getEncryptedTempFile() == null) { + Log_OC.e(TAG, "UploadFileOperation encryptedTempFile cannot be null"); + throw new NullPointerException("encryptedTempFile cannot be null"); } - try { - size = channel.size(); - } catch (IOException e1) { - size = new File(mFile.getStoragePath()).length(); - } + Triple channelResult = initFileChannel(result, fileLock, e2eFiles); + fileLock = channelResult.getFirst(); + result = channelResult.getSecond(); + FileChannel channel = channelResult.getThird(); + size = getChannelSize(channel); updateSize(size); + setUploadOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); - /// perform the upload - if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { - boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); - - mUploadOperation = new ChunkedFileUploadRemoteOperation(encryptedTempFile.getAbsolutePath(), - mFile.getParentRemotePath() + encryptedFileName, - mFile.getMimeType(), - mFile.getEtagInConflict(), - lastModifiedTimestamp, - onWifiConnection, - token, - creationTimestamp, - mDisableRetries - ); - } else { - mUploadOperation = new UploadFileRemoteOperation(encryptedTempFile.getAbsolutePath(), - mFile.getParentRemotePath() + encryptedFileName, - mFile.getMimeType(), - mFile.getEtagInConflict(), - lastModifiedTimestamp, - creationTimestamp, - token, - mDisableRetries - ); - } - - for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { - mUploadOperation.addDataTransferProgressListener(mDataTransferListener); - } - - if (mCancellationRequested.get()) { - throw new OperationCancelledException(); - } - - result = mUploadOperation.execute(client); - - /// move local temporal file or original file to its corresponding - // location in the Nextcloud local folder - if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); - } + result = performE2EUpload(clientData); if (result.isSuccess()) { - mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + originalFile.getName()); - mFile.setRemotePath(parentFile.getRemotePath() + encryptedFileName); - - - if (object instanceof DecryptedFolderMetadataFileV1 metadata) { - // update metadata - DecryptedFile decryptedFile = new DecryptedFile(); - Data data = new Data(); - data.setFilename(mFile.getDecryptedFileName()); - data.setMimetype(mFile.getMimeType()); - data.setKey(EncryptionUtils.encodeBytesToBase64String(key)); - decryptedFile.setEncrypted(data); - decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv)); - decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag()); - - metadata.getFiles().put(encryptedFileName, decryptedFile); - - EncryptedFolderMetadataFileV1 encryptedFolderMetadata = - EncryptionUtils.encryptFolderMetadata(metadata, - publicKey, - parentFile.getLocalId(), - user, - arbitraryDataProvider - ); - - String serializedFolderMetadata; - - // check if we need metadataKeys - if (metadata.getMetadata().getMetadataKey() != null) { - serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); - } else { - serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); - } - - // upload metadata - EncryptionUtils.uploadMetadata(parentFile, - serializedFolderMetadata, - token, - client, - metadataExists, - E2EVersion.V1_2, - "", - arbitraryDataProvider, - user); - } else { - DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; - encryptionUtilsV2.addFileToMetadata( - encryptedFileName, - mFile, - iv, - encryptedFile.getAuthenticationTag(), - key, - metadata, - getStorageManager()); - - // upload metadata - encryptionUtilsV2.serializeAndUploadMetadata(parentFile, - metadata, - token, - client, - true, - mContext, - user, - getStorageManager()); - } + updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); } } catch (FileNotFoundException e) { - Log_OC.d(TAG, mFile.getStoragePath() + " does not exist anymore"); + Log_OC.e(TAG, mFile.getStoragePath() + " does not exist anymore"); result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); } catch (OverlappingFileLockException e) { - Log_OC.d(TAG, "Overlapping file lock exception"); + Log_OC.e(TAG, "Overlapping file lock exception"); result = new RemoteOperationResult(ResultCode.LOCK_FAILED); } catch (Exception e) { + Log_OC.e(TAG, "UploadFileOperation exception: " + e.getLocalizedMessage()); result = new RemoteOperationResult(e); } finally { - mUploadStarted.set(false); - sendRefreshFolderEventBroadcast(); - - if (fileLock != null) { - try { - fileLock.release(); - } catch (IOException e) { - Log_OC.e(TAG, "Failed to unlock file with path " + mFile.getStoragePath()); - } - } - - if (temporalFile != null && !originalFile.equals(temporalFile)) { - temporalFile.delete(); - } - if (result == null) { - result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); - } - - logResult(result, mFile.getStoragePath(), mFile.getRemotePath()); - - // Unlock must be done otherwise folder stays locked and user can't upload any file - RemoteOperationResult unlockFolderResult; - if (object instanceof DecryptedFolderMetadataFileV1) { - unlockFolderResult = EncryptionUtils.unlockFolderV1(parentFile, client, token); - } else { - unlockFolderResult = EncryptionUtils.unlockFolder(parentFile, client, token); - } - - if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { - result = unlockFolderResult; - } - - if (encryptedTempFile != null) { - boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); - Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); - } else { - Log_OC.e(TAG, "Encrypted temp file cannot be found"); - } + result = cleanupE2EUpload(fileLock, e2eFiles, result, object, client, token); } - if (result.isSuccess()) { - handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); - } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { - getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); + completeE2EUpload(result, e2eFiles, client); + + return result; + } + + private boolean isEndToEndVersionAtLeastV2() { + return getE2EVersion().compareTo(E2EVersion.V2_0) >= 0; + } + + private E2EVersion getE2EVersion() { + return CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion(); + } + + private long getE2ECounter(OCFile parentFile) { + long counter = -1; + + if (isEndToEndVersionAtLeastV2()) { + counter = parentFile.getE2eCounter() + 1; } - // delete temporal file - if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) { - Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath()); + return counter; + } + + private String getFolderUnlockTokenOrLockFolder(OwnCloudClient client, OCFile parentFile, long counter) throws UploadException { + if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { + return mFolderUnlockToken; + } + + String token = EncryptionUtils.lockFolder(parentFile, client, counter); + mUpload.setFolderUnlockToken(token); + uploadsStorageManager.updateUpload(mUpload); + + return token; + } + + private DecryptedFolderMetadataFileV1 getDecryptedFolderMetadataV1(String publicKey, Object object) + throws NoSuchPaddingException, IllegalBlockSizeException, CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + + DecryptedFolderMetadataFileV1 metadata = new DecryptedFolderMetadataFileV1(); + metadata.setMetadata(new DecryptedMetadata()); + metadata.getMetadata().setVersion(1.2); + metadata.getMetadata().setMetadataKeys(new HashMap<>()); + String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey()); + String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey); + metadata.getMetadata().setMetadataKey(encryptedMetadataKey); + + if (object instanceof DecryptedFolderMetadataFileV1) { + metadata = (DecryptedFolderMetadataFileV1) object; + } + + return metadata; + } + + private List getCollidedFileNames(Object object) { + List result = new ArrayList<>(); + + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + for (DecryptedFile file : metadata.getFiles().values()) { + result.add(file.getEncrypted().getFilename()); + } + } else if (object instanceof DecryptedFolderMetadataFile metadataFile) { + Map files = metadataFile.getMetadata().getFiles(); + for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : files.values()) { + result.add(file.getFilename()); + } } return result; } - private void sendRefreshFolderEventBroadcast() { - Intent intent = new Intent(REFRESH_FOLDER_EVENT_RECEIVER); - LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); + private String getEncryptedFileName(Object object) { + String encryptedFileName = EncryptionUtils.generateUid(); + + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + while (metadata.getFiles().get(encryptedFileName) != null) { + encryptedFileName = EncryptionUtils.generateUid(); + } + } else { + while (((DecryptedFolderMetadataFile) object).getMetadata().getFiles().get(encryptedFileName) != null) { + encryptedFileName = EncryptionUtils.generateUid(); + } + } + + return encryptedFileName; } + private void setUploadOperationForE2E(String token, + File encryptedTempFile, + String encryptedFileName, + long lastModifiedTimestamp, + long creationTimestamp, + long size) { + + if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { + boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); + + mUploadOperation = new ChunkedFileUploadRemoteOperation(encryptedTempFile.getAbsolutePath(), + mFile.getParentRemotePath() + encryptedFileName, + mFile.getMimeType(), + mFile.getEtagInConflict(), + lastModifiedTimestamp, + onWifiConnection, + token, + creationTimestamp, + mDisableRetries + ); + } else { + mUploadOperation = new UploadFileRemoteOperation(encryptedTempFile.getAbsolutePath(), + mFile.getParentRemotePath() + encryptedFileName, + mFile.getMimeType(), + mFile.getEtagInConflict(), + lastModifiedTimestamp, + creationTimestamp, + token, + mDisableRetries + ); + } + } + + private Triple initFileChannel(RemoteOperationResult result, FileLock fileLock, E2EFiles e2eFiles) throws IOException { + FileChannel channel = null; + + try (RandomAccessFile randomAccessFile = new RandomAccessFile(mFile.getStoragePath(), "rw")) { + channel = randomAccessFile.getChannel(); + fileLock = channel.tryLock(); + } catch (IOException ioException) { + Log_OC.d(TAG, "Error caught at getChannelFromFile: " + ioException); + + // this basically means that the file is on SD card + // try to copy file to temporary dir if it doesn't exist + String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + + mFile.getRemotePath(); + mFile.setStoragePath(temporalPath); + e2eFiles.setTemporalFile(new File(temporalPath)); + + if (e2eFiles.getTemporalFile() == null) { + throw new NullPointerException("Original file cannot be null"); + } + + Files.deleteIfExists(Paths.get(temporalPath)); + result = copy(e2eFiles.getOriginalFile(), e2eFiles.getTemporalFile()); + + if (result.isSuccess()) { + if (e2eFiles.getTemporalFile().length() == e2eFiles.getOriginalFile().length()) { + try (RandomAccessFile randomAccessFile = new RandomAccessFile(e2eFiles.getTemporalFile().getAbsolutePath(), "rw")) { + channel = randomAccessFile.getChannel(); + fileLock = channel.tryLock(); + } catch (IOException e) { + Log_OC.d(TAG, "Error caught at getChannelFromFile: " + e); + } + } else { + result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + } + } + } + + return new Triple<>(fileLock, result, channel); + } + + private long getChannelSize(FileChannel channel) { + try { + return channel.size(); + } catch (IOException e1) { + return new File(mFile.getStoragePath()).length(); + } + } + + private RemoteOperationResult performE2EUpload(E2EClientData data) throws OperationCancelledException { + for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { + mUploadOperation.addDataTransferProgressListener(mDataTransferListener); + } + + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } + + RemoteOperationResult result = mUploadOperation.execute(data.getClient()); + + /// move local temporal file or original file to its corresponding + // location in the Nextcloud local folder + if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { + result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + } + + return result; + } + + private E2EData getE2EData(Object object) throws InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidParameterSpecException, IOException { + byte[] key = EncryptionUtils.generateKey(); + byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); + Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); + File file = new File(mFile.getStoragePath()); + EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher); + String encryptedFileName = getEncryptedFileName(object); + + if (key == null) { + throw new NullPointerException("key cannot be null"); + } + + return new E2EData(key, iv, encryptedFile, encryptedFileName); + } + + private void updateMetadataForE2E(Object object, E2EData e2eData, E2EClientData clientData, E2EFiles e2eFiles, ArbitraryDataProvider arbitraryDataProvider, EncryptionUtilsV2 encryptionUtilsV2, boolean metadataExists) + + throws InvalidAlgorithmParameterException, UploadException, NoSuchPaddingException, IllegalBlockSizeException, CertificateException, + NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + + final var filename = new File(mFile.getRemotePath()).getName(); + mFile.setDecryptedRemotePath(e2eFiles.getParentFile().getDecryptedRemotePath() + filename); + mFile.setRemotePath(e2eFiles.getParentFile().getRemotePath() + e2eData.getEncryptedFileName()); + + + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + updateMetadataForV1(metadata, + e2eData, + clientData, + e2eFiles.getParentFile(), + arbitraryDataProvider, + metadataExists); + } else if (object instanceof DecryptedFolderMetadataFile metadata) { + updateMetadataForV2(metadata, + encryptionUtilsV2, + e2eData, + clientData, + e2eFiles.getParentFile()); + } + } + + private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData e2eData, E2EClientData clientData, + OCFile parentFile, ArbitraryDataProvider arbitraryDataProvider, boolean metadataExists) + + throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, + CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, UploadException { + + DecryptedFile decryptedFile = new DecryptedFile(); + Data data = new Data(); + data.setFilename(mFile.getDecryptedFileName()); + data.setMimetype(mFile.getMimeType()); + data.setKey(EncryptionUtils.encodeBytesToBase64String(e2eData.getKey())); + decryptedFile.setEncrypted(data); + decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(e2eData.getIv())); + decryptedFile.setAuthenticationTag(e2eData.getEncryptedFile().getAuthenticationTag()); + + metadata.getFiles().put(e2eData.getEncryptedFileName(), decryptedFile); + + EncryptedFolderMetadataFileV1 encryptedFolderMetadata = + EncryptionUtils.encryptFolderMetadata(metadata, + clientData.getPublicKey(), + parentFile.getLocalId(), + user, + arbitraryDataProvider + ); + + String serializedFolderMetadata; + + if (metadata.getMetadata().getMetadataKey() != null) { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); + } else { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); + } + + // upload metadata + EncryptionUtils.uploadMetadata(parentFile, + serializedFolderMetadata, + clientData.getToken(), + clientData.getClient(), + metadataExists, + E2EVersion.V1_2, + "", + arbitraryDataProvider, + user); + } + + + private void updateMetadataForV2(DecryptedFolderMetadataFile metadata, EncryptionUtilsV2 encryptionUtilsV2, E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { + encryptionUtilsV2.addFileToMetadata( + e2eData.getEncryptedFileName(), + mFile, + e2eData.getIv(), + e2eData.getEncryptedFile().getAuthenticationTag(), + e2eData.getKey(), + metadata, + getStorageManager()); + + // upload metadata + encryptionUtilsV2.serializeAndUploadMetadata(parentFile, + metadata, + clientData.getToken(), + clientData.getClient(), + true, + mContext, + user, + getStorageManager()); + } + + private void completeE2EUpload(RemoteOperationResult result, E2EFiles e2eFiles, OwnCloudClient client) { + if (result.isSuccess()) { + handleLocalBehaviour(e2eFiles.getTemporalFile(), e2eFiles.getExpectedFile(), e2eFiles.getOriginalFile(), client); + } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { + getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); + } + + e2eFiles.deleteTemporalFile(); + } + + private RemoteOperationResult cleanupE2EUpload(FileLock fileLock, E2EFiles e2eFiles, RemoteOperationResult result, Object object, OwnCloudClient client, String token) { + mUploadStarted.set(false); + + if (fileLock != null) { + try { + fileLock.release(); + } catch (IOException e) { + Log_OC.e(TAG, "Failed to unlock file with path " + mFile.getStoragePath()); + } + } + + e2eFiles.deleteTemporalFileWithOriginalFileComparison(); + + if (result == null) { + result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); + } + + logResult(result, mFile.getStoragePath(), mFile.getRemotePath()); + + // Unlock must be done otherwise folder stays locked and user can't upload any file + RemoteOperationResult unlockFolderResult; + if (object instanceof DecryptedFolderMetadataFileV1) { + unlockFolderResult = EncryptionUtils.unlockFolderV1(e2eFiles.getParentFile(), client, token); + } else { + unlockFolderResult = EncryptionUtils.unlockFolder(e2eFiles.getParentFile(), client, token); + } + + if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { + result = unlockFolderResult; + } + + if (unlockFolderResult != null && unlockFolderResult.isSuccess()) { + Log_OC.d(TAG, "Folder successfully unlocked: " + e2eFiles.getParentFile().getFileName()); + + if (duplicatedEncryptedFile != null) { + FileUploadHelper.Companion.instance().removeDuplicatedFile(duplicatedEncryptedFile, client, user, () -> { + duplicatedEncryptedFile = null; + return Unit.INSTANCE; + }); + } + } + + e2eFiles.deleteEncryptedTempFile(); + + return result; + } + // endregion + private RemoteOperationResult checkConditions(File originalFile) { RemoteOperationResult remoteOperationResult = null; @@ -836,7 +983,7 @@ public class UploadFileOperation extends SyncOperation { } // check name collision - RemoteOperationResult collisionResult = checkNameCollision(client, null, false); + final var collisionResult = checkNameCollision(null, client, null, false); if (collisionResult != null) { result = collisionResult; return collisionResult; @@ -874,14 +1021,15 @@ public class UploadFileOperation extends SyncOperation { channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel(); fileLock = channel.tryLock(); } else { - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } } } try { size = channel.size(); - } catch (Exception e1) { + } catch (Exception exception) { + Log_OC.e(TAG, "normalUpload, size cannot be determined from channel: " + exception); size = new File(mFile.getStoragePath()).length(); } @@ -909,6 +1057,10 @@ public class UploadFileOperation extends SyncOperation { mDisableRetries); } + /** + * Adds the onTransferProgress in FileUploadWorker + * {@link FileUploadWorker#onTransferProgress(long, long, long, String)()} + */ for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { mUploadOperation.addDataTransferProgressListener(mDataTransferListener); } @@ -923,17 +1075,17 @@ public class UploadFileOperation extends SyncOperation { /// move local temporal file or original file to its corresponding // location in the Nextcloud local folder if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + result = new RemoteOperationResult<>(ResultCode.SYNC_CONFLICT); } } } catch (FileNotFoundException e) { Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); - result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); + result = new RemoteOperationResult<>(ResultCode.LOCAL_FILE_NOT_FOUND); } catch (OverlappingFileLockException e) { Log_OC.d(TAG, "Overlapping file lock exception"); - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } catch (Exception e) { - result = new RemoteOperationResult(e); + result = new RemoteOperationResult<>(e); } finally { mUploadStarted.set(false); @@ -954,18 +1106,19 @@ public class UploadFileOperation extends SyncOperation { } if (temporalFile != null && !originalFile.equals(temporalFile)) { - temporalFile.delete(); + boolean isTempFileDeleted = temporalFile.delete(); + Log_OC.d(TAG, "normalUpload, temp folder deletion: " + isTempFileDeleted); } if (result == null) { - result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); + result = new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); } logResult(result, mOriginalStoragePath, mRemotePath); } if (result.isSuccess()) { - handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); + handleLocalBehaviour(temporalFile, expectedFile, originalFile, client); } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); } @@ -1023,7 +1176,8 @@ public class UploadFileOperation extends SyncOperation { } @CheckResult - private RemoteOperationResult checkNameCollision(OwnCloudClient client, + private RemoteOperationResult checkNameCollision(OCFile parentFile, + OwnCloudClient client, List fileNames, boolean encrypted) throws OperationCancelledException { @@ -1044,6 +1198,10 @@ public class UploadFileOperation extends SyncOperation { } break; case OVERWRITE: + if (parentFile != null && encrypted) { + duplicatedEncryptedFile = getStorageManager().findDuplicatedFile(parentFile, mFile); + } + Log_OC.d(TAG, "Overwriting file"); break; case ASK_USER: @@ -1059,17 +1217,32 @@ public class UploadFileOperation extends SyncOperation { return null; } - private void handleSuccessfulUpload(File temporalFile, - File expectedFile, - File originalFile, - OwnCloudClient client) { - switch (mLocalBehaviour) { - case FileUploadWorker.LOCAL_BEHAVIOUR_FORGET: - default: - mFile.setStoragePath(""); - saveUploadedFile(client); - break; + public void handleLocalBehaviour() { + if (user == null || mFile == null || mContext == null) { + Log_OC.d(TAG, "handleLocalBehaviour: user, file, or context is null."); + return; + } + final var client = getClient(); + if (client == null) { + Log_OC.d(TAG, "handleLocalBehaviour: client is null"); + return; + } + + String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); + File expectedFile = new File(expectedPath); + File originalFile = new File(mOriginalStoragePath); + String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + mFile.getRemotePath(); + File temporalFile = new File(temporalPath); + + handleLocalBehaviour(temporalFile, expectedFile, originalFile, client); + } + + private void handleLocalBehaviour(File temporalFile, + File expectedFile, + File originalFile, + OwnCloudClient client) { + switch (mLocalBehaviour) { case FileUploadWorker.LOCAL_BEHAVIOUR_DELETE: originalFile.delete(); mFile.setStoragePath(""); @@ -1114,9 +1287,18 @@ public class UploadFileOperation extends SyncOperation { FileDataStorageManager.triggerMediaScan(newFile.getAbsolutePath()); } break; + + default: + mFile.setStoragePath(""); + saveUploadedFile(client); + break; } } + private OCCapability getCapabilities() { + return CapabilityUtils.getCapability(mContext); + } + /** * Checks the existence of the folder where the current file will be uploaded both in the remote server and in the * local database. @@ -1200,7 +1382,7 @@ public class UploadFileOperation extends SyncOperation { * @param fileNames list of decrypted file names * @return new remote path */ - private String getNewAvailableRemotePath(OwnCloudClient client, + public static String getNewAvailableRemotePath(OwnCloudClient client, String remotePath, List fileNames, boolean encrypted) { @@ -1226,7 +1408,7 @@ public class UploadFileOperation extends SyncOperation { return newPath; } - private boolean existsFile(OwnCloudClient client, + private static boolean existsFile(OwnCloudClient client, String remotePath, List fileNames, boolean encrypted) { @@ -1372,22 +1554,16 @@ public class UploadFileOperation extends SyncOperation { if (!sourceFile.renameTo(targetFile)) { // try to copy and then delete targetFile.createNewFile(); - FileChannel inChannel = new FileInputStream(sourceFile).getChannel(); - FileChannel outChannel = new FileOutputStream(targetFile).getChannel(); - try { + try ( + FileChannel inChannel = new FileInputStream(sourceFile).getChannel(); + FileChannel outChannel = new FileOutputStream(targetFile).getChannel() + ) { inChannel.transferTo(0, inChannel.size(), outChannel); sourceFile.delete(); } catch (Exception e) { mFile.setStoragePath(""); // forget the local file // by now, treat this as a success; the file was uploaded // the best option could be show a warning message - } finally { - if (inChannel != null) { - inChannel.close(); - } - if (outChannel != null) { - outChannel.close(); - } } } @@ -1469,6 +1645,7 @@ public class UploadFileOperation extends SyncOperation { file.setEtag(remoteFile.getEtag()); file.setRemoteId(remoteFile.getRemoteId()); file.setPermissions(remoteFile.getPermissions()); + file.setUploadTimestamp(remoteFile.getUploadTimestamp()); } public interface OnRenameListener { diff --git a/app/src/main/java/com/owncloud/android/operations/common/SyncOperation.java b/app/src/main/java/com/owncloud/android/operations/common/SyncOperation.java index ea71c44..27dfbd1 100644 --- a/app/src/main/java/com/owncloud/android/operations/common/SyncOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/common/SyncOperation.java @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2014 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.operations.common; @@ -46,8 +46,7 @@ public abstract class SyncOperation extends RemoteOperation { */ public RemoteOperationResult execute(Context context) { if (storageManager.getUser().isAnonymous()) { - throw new IllegalArgumentException("Trying to execute a sync operation with a " + - "storage manager for an anonymous account"); + return new RemoteOperationResult(RemoteOperationResult.ResultCode.ACCOUNT_EXCEPTION); } return super.execute(this.storageManager.getUser(), context); } diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt new file mode 100644 index 0000000..892152a --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.e2e + +import com.owncloud.android.lib.common.OwnCloudClient + +data class E2EClientData(val client: OwnCloudClient, val token: String, val publicKey: String) diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt new file mode 100644 index 0000000..003d216 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt @@ -0,0 +1,17 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.e2e + +import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFile + +data class E2EData( + val key: ByteArray, + val iv: ByteArray, + val encryptedFile: EncryptedFile, + val encryptedFileName: String +) diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt new file mode 100644 index 0000000..d67a095 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.e2e + +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import java.io.File + +data class E2EFiles( + var parentFile: OCFile, + var temporalFile: File?, + var originalFile: File, + var expectedFile: File?, + var encryptedTempFile: File? +) { + private val tag = "E2EFiles" + + fun deleteTemporalFile() { + if (temporalFile?.exists() == true && temporalFile?.delete() == false) { + Log_OC.e(tag, "Could not delete temporal file " + temporalFile?.absolutePath) + } + } + + fun deleteTemporalFileWithOriginalFileComparison() { + if (originalFile == temporalFile) { + return + } + + val isTemporalFileDeleted = temporalFile?.delete() + Log_OC.d(tag, "isTemporalFileDeleted: $isTemporalFileDeleted") + } + + fun deleteEncryptedTempFile() { + if (encryptedTempFile != null) { + val isTempEncryptedFileDeleted = encryptedTempFile?.delete() + Log_OC.e(tag, "isTempEncryptedFileDeleted: $isTempEncryptedFileDeleted") + } else { + Log_OC.e(tag, "Encrypted temp file cannot be found") + } + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiver.kt b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiver.kt new file mode 100644 index 0000000..2d35262 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiver.kt @@ -0,0 +1,61 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.operations.upload + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.annotation.RequiresApi +import androidx.core.content.IntentCompat +import androidx.core.net.toUri +import com.owncloud.android.operations.UploadFileOperation + +class UploadFileBroadcastReceiver : BroadcastReceiver() { + companion object { + const val ACTION_TYPE = "UploadFileBroadcastReceiver.ACTION_TYPE" + } + + override fun onReceive(context: Context, intent: Intent) { + val actionType = + IntentCompat.getSerializableExtra(intent, ACTION_TYPE, UploadFileBroadcastReceiverActions::class.java) + ?: return + + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel(UploadFileOperation.MISSING_FILE_PERMISSION_NOTIFICATION_ID) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && + actionType == UploadFileBroadcastReceiverActions.ALLOW_ALL_FILES + ) { + redirectToAllFilesAccess(context) + } else { + redirectToAppInfo(context) + } + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun redirectToAllFilesAccess(context: Context) { + Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply { + data = "package:${context.packageName}".toUri() + flags = Intent.FLAG_ACTIVITY_NEW_TASK + }.run { + context.startActivity(this) + } + } + + private fun redirectToAppInfo(context: Context) { + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", context.packageName, null) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + }.run { + context.startActivity(this) + } + } +} diff --git a/app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiverActions.kt b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiverActions.kt new file mode 100644 index 0000000..c0d00d4 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileBroadcastReceiverActions.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.upload + +enum class UploadFileBroadcastReceiverActions : java.io.Serializable { + ALLOW_ALL_FILES, + APP_PERMISSIONS +} diff --git a/app/src/main/java/com/owncloud/android/operations/upload/UploadFileException.kt b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileException.kt new file mode 100644 index 0000000..880a5eb --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileException.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.upload + +sealed class UploadFileException(message: String) : Exception(message) { + class EmptyOrNullFilePath : UploadFileException("Empty or null file path") + class MissingPermission : UploadFileException("Missing storage permission") +} diff --git a/app/src/main/java/com/owncloud/android/operations/upload/UploadFileOperationExtensions.kt b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileOperationExtensions.kt new file mode 100644 index 0000000..72ba621 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/upload/UploadFileOperationExtensions.kt @@ -0,0 +1,76 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.operations.upload + +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat +import com.owncloud.android.R +import com.owncloud.android.operations.UploadFileOperation +import com.owncloud.android.operations.UploadFileOperation.MISSING_FILE_PERMISSION_NOTIFICATION_ID +import com.owncloud.android.ui.notifications.NotificationUtils + +fun UploadFileOperation.showStoragePermissionNotification() { + val notificationManager = ContextCompat.getSystemService(context, NotificationManager::class.java) + ?: return + val alreadyShown = notificationManager.activeNotifications.any { + it.id == MISSING_FILE_PERMISSION_NOTIFICATION_ID + } + if (alreadyShown) { + return + } + + val allowAllFileAccessAction = getAllowAllFileAccessAction(context) + val appPermissionsAction = getAppPermissionsAction(context) + + val notificationBuilder = + NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setContentTitle(context.getString(R.string.upload_missing_storage_permission_title)) + .setContentText(context.getString(R.string.upload_missing_storage_permission_description)) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .addAction(allowAllFileAccessAction) + .addAction(appPermissionsAction) + .setAutoCancel(true) + + notificationManager.notify(MISSING_FILE_PERMISSION_NOTIFICATION_ID, notificationBuilder.build()) +} + +private fun getActionPendingIntent(context: Context, actionType: UploadFileBroadcastReceiverActions): PendingIntent { + val intent = Intent(context, UploadFileBroadcastReceiver::class.java).apply { + action = "com.owncloud.android.ACTION_UPLOAD_FILE_PERMISSION" + putExtra(UploadFileBroadcastReceiver.ACTION_TYPE, actionType) + } + + return PendingIntent.getBroadcast( + context, + actionType.ordinal, + intent, + PendingIntent.FLAG_IMMUTABLE + ) +} + +private fun getAllowAllFileAccessAction(context: Context): NotificationCompat.Action { + val pendingIntent = getActionPendingIntent(context, UploadFileBroadcastReceiverActions.ALLOW_ALL_FILES) + return NotificationCompat.Action( + null, + context.getString(R.string.upload_missing_storage_permission_allow_file_access), + pendingIntent + ) +} + +private fun getAppPermissionsAction(context: Context): NotificationCompat.Action { + val pendingIntent = getActionPendingIntent(context, UploadFileBroadcastReceiverActions.APP_PERMISSIONS) + return NotificationCompat.Action( + null, + context.getString(R.string.upload_missing_storage_permission_app_permissions), + pendingIntent + ) +} diff --git a/app/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java b/app/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java index 9053562..246672f 100644 --- a/app/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java +++ b/app/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.providers; diff --git a/app/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java b/app/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java index cc8b20a..3ad48e4 100644 --- a/app/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java +++ b/app/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2020-2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2019-2021 Chris Narkiewicz * SPDX-FileCopyrightText: 2016 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.providers; @@ -25,7 +25,6 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract; import android.provider.DocumentsProvider; -import android.widget.Toast; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; @@ -34,6 +33,8 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.nextcloud.client.utils.HashUtil; +import com.nextcloud.utils.extensions.ContextExtensionsKt; +import com.nextcloud.utils.fileNameValidator.FileNameValidator; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -47,6 +48,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.CheckEtagRemoteOperation; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; +import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.operations.CopyFileOperation; import com.owncloud.android.operations.CreateFolderOperation; import com.owncloud.android.operations.DownloadFileOperation; @@ -59,6 +61,7 @@ import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.FileUtil; import com.owncloud.android.utils.MimeTypeUtil; +import com.owncloud.android.utils.theme.CapabilityUtils; import org.nextcloud.providers.cursors.FileCursor; import org.nextcloud.providers.cursors.RootCursor; @@ -95,6 +98,8 @@ public class DocumentsStorageProvider extends DocumentsProvider { @Inject UserAccountManager accountManager; + private boolean isFolderPathValid = true; + @VisibleForTesting static final String DOCUMENTID_SEPARATOR = "/"; private static final int DOCUMENTID_PARTS = 2; @@ -152,17 +157,25 @@ public class DocumentsStorageProvider extends DocumentsProvider { Document parentFolder = toDocument(parentDocumentId); final FileCursor resultCursor = new FileCursor(projection); + if (!parentFolder.getFile().canRead()) { + showToast(R.string.document_storage_provider_cannot_read); + return resultCursor; + } + if (parentFolder.getFile().isEncrypted() && !FileOperationsHelper.isEndToEndEncryptionSetup(context, parentFolder.getUser())) { - Toast.makeText(context, R.string.e2e_not_yet_setup, Toast.LENGTH_LONG).show(); + showToast(R.string.e2e_not_yet_setup); return resultCursor; } FileDataStorageManager storageManager = parentFolder.getStorageManager(); - for (OCFile file : storageManager.getFolderContent(parentFolder.getFile(), false)) { - resultCursor.addFile(new Document(storageManager, file)); + if (file.canRead()) { + resultCursor.addFile(new Document(storageManager, file)); + } else { + Log_OC.w(TAG,"Skipping file, doesn't have read permission. RemotePath: " + file.getRemotePath()); + } } boolean isLoading = false; @@ -187,6 +200,11 @@ public class DocumentsStorageProvider extends DocumentsProvider { throws FileNotFoundException { Log_OC.d(TAG, "openDocument(), id=" + documentId); + if (!isFolderPathValid) { + Log_OC.d(TAG, "Folder path is not valid, operation is cancelled"); + return null; + } + Document document = toDocument(documentId); Context context = getNonNullContext(); @@ -206,13 +224,11 @@ public class DocumentsStorageProvider extends DocumentsProvider { final AtomicBoolean downloadResult = new AtomicBoolean(false); final Thread downloadThread = new Thread(() -> { DownloadFileOperation downloadFileOperation = new DownloadFileOperation(user, ocFile, context); - RemoteOperationResult result = downloadFileOperation.execute(document.getClient()); + final var result = downloadFileOperation.execute(document.getClient()); if (!result.isSuccess()) { if (ocFile.isDown()) { Handler handler = new Handler(Looper.getMainLooper()); - handler.post(() -> Toast.makeText(MainApp.getAppContext(), - R.string.file_not_synced, - Toast.LENGTH_SHORT).show()); + handler.post(() -> showToast(R.string.file_not_synced)); downloadResult.set(true); } else { Log_OC.e(TAG, result.toString()); @@ -275,16 +291,14 @@ public class DocumentsStorageProvider extends DocumentsProvider { OCFile ocFile = document.getFile(); RemoteOperationResult result = new CheckEtagRemoteOperation(ocFile.getRemotePath(), ocFile.getEtag()) .execute(document.getUser(), context); - switch (result.getCode()) { - case ETAG_CHANGED: - return true; - case ETAG_UNCHANGED: - return false; - case FILE_NOT_FOUND: - default: + return switch (result.getCode()) { + case ETAG_CHANGED -> result.getData() != null; + case ETAG_UNCHANGED -> false; + default -> { Log_OC.e(TAG, result.toString()); throw new FileNotFoundException("Error synchronizing file: " + ocFile.getFileName()); - } + } + }; } /** @@ -347,9 +361,19 @@ public class DocumentsStorageProvider extends DocumentsProvider { public String renameDocument(String documentId, String displayName) throws FileNotFoundException { Log_OC.d(TAG, "renameDocument(), id=" + documentId); - Document document = toDocument(documentId); + String errorMessage = checkFileName(displayName); + if (errorMessage != null) { + showToast(errorMessage); + return null; + } - RemoteOperationResult result = new RenameFileOperation(document.getRemotePath(), + Document document = toDocument(documentId); + if (!document.getFile().canRename()) { + showToast(R.string.document_storage_provider_cannot_rename); + return null; + } + + final var result = new RenameFileOperation(document.getRemotePath(), displayName, document.getStorageManager()) .execute(document.getClient()); @@ -370,12 +394,18 @@ public class DocumentsStorageProvider extends DocumentsProvider { public String copyDocument(String sourceDocumentId, String targetParentDocumentId) throws FileNotFoundException { Log_OC.d(TAG, "copyDocument(), id=" + sourceDocumentId); - Document document = toDocument(sourceDocumentId); - - FileDataStorageManager storageManager = document.getStorageManager(); Document targetFolder = toDocument(targetParentDocumentId); - RemoteOperationResult result = new CopyFileOperation(document.getRemotePath(), + String filename = targetFolder.getFile().getFileName(); + isFolderPathValid = checkFolderPath(filename); + if (!isFolderPathValid) { + showToast(R.string.file_name_validator_error_contains_reserved_names_or_invalid_characters); + return null; + } + + Document document = toDocument(sourceDocumentId); + FileDataStorageManager storageManager = document.getStorageManager(); + final var result = new CopyFileOperation(document.getRemotePath(), targetFolder.getRemotePath(), document.getStorageManager()) .execute(document.getClient()); @@ -389,7 +419,7 @@ public class DocumentsStorageProvider extends DocumentsProvider { Context context = getNonNullContext(); User user = document.getUser(); - RemoteOperationResult updateParent = new RefreshFolderOperation(targetFolder.getFile(), + final var updateParent = new RefreshFolderOperation(targetFolder.getFile(), System.currentTimeMillis(), false, false, @@ -422,10 +452,22 @@ public class DocumentsStorageProvider extends DocumentsProvider { throws FileNotFoundException { Log_OC.d(TAG, "moveDocument(), id=" + sourceDocumentId); - Document document = toDocument(sourceDocumentId); Document targetFolder = toDocument(targetParentDocumentId); - RemoteOperationResult result = new MoveFileOperation(document.getRemotePath(), + String filename = targetFolder.getFile().getFileName(); + isFolderPathValid = checkFolderPath(filename); + if (!isFolderPathValid) { + showToast(R.string.file_name_validator_error_contains_reserved_names_or_invalid_characters); + return null; + } + + Document document = toDocument(sourceDocumentId); + if (!document.getFile().canMove()) { + showToast(R.string.document_storage_provider_cannot_move); + return null; + } + + final var result = new MoveFileOperation(document.getRemotePath(), targetFolder.getRemotePath(), document.getStorageManager()) .execute(document.getClient()); @@ -463,11 +505,33 @@ public class DocumentsStorageProvider extends DocumentsProvider { return result; } + private OCCapability getCapabilities() { + return CapabilityUtils.getCapability(accountManager.getUser(), getNonNullContext()); + } + + private boolean checkFolderPath(String filename) { + return FileNameValidator.INSTANCE.checkFolderPath(filename, getCapabilities(), getNonNullContext()); + } + + private String checkFileName(String filename) { + return FileNameValidator.INSTANCE.checkFileName(filename, getCapabilities(), getNonNullContext(),null); + } + @Override public String createDocument(String documentId, String mimeType, String displayName) throws FileNotFoundException { Log_OC.d(TAG, "createDocument(), id=" + documentId); + String errorMessage = checkFileName(displayName); + if (errorMessage != null) { + showToast(errorMessage); + return null; + } + Document folderDocument = toDocument(documentId); + if (!folderDocument.getFile().canCreateFileAndFolder()) { + showToast(R.string.document_storage_provider_cannot_create_file_and_folder); + return null; + } if (DocumentsContract.Document.MIME_TYPE_DIR.equalsIgnoreCase(mimeType)) { return createFolder(folderDocument, displayName); @@ -477,12 +541,16 @@ public class DocumentsStorageProvider extends DocumentsProvider { } private String createFolder(Document targetFolder, String displayName) throws FileNotFoundException { + if (!targetFolder.getFile().canCreateFileAndFolder()) { + showToast(R.string.document_storage_provider_cannot_create_folder_inside_folder); + return null; + } Context context = getNonNullContext(); String newDirPath = targetFolder.getRemotePath() + displayName + PATH_SEPARATOR; FileDataStorageManager storageManager = targetFolder.getStorageManager(); - RemoteOperationResult result = new CreateFolderOperation(newDirPath, + final var result = new CreateFolderOperation(newDirPath, accountManager.getUser(), context, storageManager) @@ -494,7 +562,7 @@ public class DocumentsStorageProvider extends DocumentsProvider { displayName + " and documentId " + targetFolder.getDocumentId()); } - RemoteOperationResult updateParent = new RefreshFolderOperation(targetFolder.getFile(), System.currentTimeMillis(), + final var updateParent = new RefreshFolderOperation(targetFolder.getFile(), System.currentTimeMillis(), false, false, true, storageManager, targetFolder.getUser(), context) .execute(targetFolder.getClient()); @@ -512,6 +580,10 @@ public class DocumentsStorageProvider extends DocumentsProvider { } private String createFile(Document targetFolder, String displayName, String mimeType) throws FileNotFoundException { + if (!targetFolder.getFile().canCreateFileAndFolder()) { + showToast(R.string.document_storage_provider_cannot_create_file_inside_folder); + return null; + } User user = targetFolder.getUser(); @@ -542,7 +614,7 @@ public class DocumentsStorageProvider extends DocumentsProvider { // perform the upload, no need for chunked operation as we have a empty file OwnCloudClient client = targetFolder.getClient(); - RemoteOperationResult result = new UploadFileRemoteOperation(emptyFile.getAbsolutePath(), + final var result = new UploadFileRemoteOperation(emptyFile.getAbsolutePath(), newFilePath, mimeType, "", @@ -558,7 +630,7 @@ public class DocumentsStorageProvider extends DocumentsProvider { Context context = getNonNullContext(); - RemoteOperationResult updateParent = new RefreshFolderOperation(targetFolder.getFile(), + final var updateParent = new RefreshFolderOperation(targetFolder.getFile(), System.currentTimeMillis(), false, false, @@ -592,13 +664,18 @@ public class DocumentsStorageProvider extends DocumentsProvider { Context context = getNonNullContext(); Document document = toDocument(documentId); + if (!document.getFile().canDeleteOrLeaveShare()) { + showToast(R.string.document_storage_provider_cannot_delete); + return; + } + // get parent here, because it is not available anymore after the document was deleted Document parentFolder = document.getParent(); recursiveRevokePermission(document); OCFile file = document.getStorageManager().getFileByPath(document.getRemotePath()); - RemoteOperationResult result = new RemoveFileOperation(file, + final var result = new RemoveFileOperation(file, false, document.getUser(), true, @@ -840,4 +917,12 @@ public class DocumentsStorageProvider extends DocumentsProvider { return new Document(getStorageManager(), parentId); } } + + private void showToast(int messageId) { + ContextExtensionsKt.showToast(getNonNullContext(), messageId); + } + + private void showToast(String message) { + ContextExtensionsKt.showToast(getNonNullContext(), message); + } } diff --git a/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java index b98db57..19e5fc0 100644 --- a/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/app/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2013-2016 María Asensio Valverde * SPDX-FileCopyrightText: 2014 David A. Velasco * SPDX-FileCopyrightText: 2011 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.providers; @@ -120,43 +120,19 @@ public class FileContentProvider extends ContentProvider { VerificationUtils.verifyWhere(where); } - int count; - switch (mUriMatcher.match(uri)) { - case SINGLE_FILE: - count = deleteSingleFile(db, uri, where, whereArgs); - break; - case DIRECTORY: - count = deleteDirectory(db, uri, where, whereArgs); - break; - case ROOT_DIRECTORY: - count = db.delete(ProviderTableMeta.FILE_TABLE_NAME, where, whereArgs); - break; - case SHARES: - count = db.delete(ProviderTableMeta.OCSHARES_TABLE_NAME, where, whereArgs); - break; - case CAPABILITIES: - count = db.delete(ProviderTableMeta.CAPABILITIES_TABLE_NAME, where, whereArgs); - break; - case UPLOADS: - count = db.delete(ProviderTableMeta.UPLOADS_TABLE_NAME, where, whereArgs); - break; - case SYNCED_FOLDERS: - count = db.delete(ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME, where, whereArgs); - break; - case EXTERNAL_LINKS: - count = db.delete(ProviderTableMeta.EXTERNAL_LINKS_TABLE_NAME, where, whereArgs); - break; - case VIRTUAL: - count = db.delete(ProviderTableMeta.VIRTUAL_TABLE_NAME, where, whereArgs); - break; - case FILESYSTEM: - count = db.delete(ProviderTableMeta.FILESYSTEM_TABLE_NAME, where, whereArgs); - break; - default: - throw new IllegalArgumentException(String.format(Locale.US, "Unknown uri: %s", uri.toString())); - } - - return count; + return switch (mUriMatcher.match(uri)) { + case SINGLE_FILE -> deleteSingleFile(db, uri, where, whereArgs); + case DIRECTORY -> deleteDirectory(db, uri, where, whereArgs); + case ROOT_DIRECTORY -> db.delete(ProviderTableMeta.FILE_TABLE_NAME, where, whereArgs); + case SHARES -> db.delete(ProviderTableMeta.OCSHARES_TABLE_NAME, where, whereArgs); + case CAPABILITIES -> db.delete(ProviderTableMeta.CAPABILITIES_TABLE_NAME, where, whereArgs); + case UPLOADS -> db.delete(ProviderTableMeta.UPLOADS_TABLE_NAME, where, whereArgs); + case SYNCED_FOLDERS -> db.delete(ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME, where, whereArgs); + case EXTERNAL_LINKS -> db.delete(ProviderTableMeta.EXTERNAL_LINKS_TABLE_NAME, where, whereArgs); + case VIRTUAL -> db.delete(ProviderTableMeta.VIRTUAL_TABLE_NAME, where, whereArgs); + case FILESYSTEM -> db.delete(ProviderTableMeta.FILESYSTEM_TABLE_NAME, where, whereArgs); + default -> throw new IllegalArgumentException(String.format(Locale.US, "Unknown uri: %s", uri.toString())); + }; } private int deleteDirectory(SupportSQLiteDatabase db, Uri uri, String where, String... whereArgs) { @@ -218,14 +194,11 @@ public class FileContentProvider extends ContentProvider { @Override public String getType(@NonNull Uri uri) { - switch (mUriMatcher.match(uri)) { - case ROOT_DIRECTORY: - return ProviderTableMeta.CONTENT_TYPE; - case SINGLE_FILE: - return ProviderTableMeta.CONTENT_TYPE_ITEM; - default: - throw new IllegalArgumentException(String.format(Locale.US, "Unknown Uri id: %s", uri)); - } + return switch (mUriMatcher.match(uri)) { + case ROOT_DIRECTORY -> ProviderTableMeta.CONTENT_TYPE; + case SINGLE_FILE -> ProviderTableMeta.CONTENT_TYPE_ITEM; + default -> throw new IllegalArgumentException(String.format(Locale.US, "Unknown Uri id: %s", uri)); + }; } @Override @@ -372,7 +345,13 @@ public class FileContentProvider extends ContentProvider { private void updateFilesTableAccordingToShareInsertion(SupportSQLiteDatabase db, ContentValues newShare) { ContentValues fileValues = new ContentValues(); - ShareType newShareType = ShareType.fromValue(newShare.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE)); + Integer shareTypeValue = newShare.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE); + if (shareTypeValue == null) { + Log_OC.w(TAG, "Share type is null. Skipping file update."); + return; + } + + ShareType newShareType = ShareType.fromValue(shareTypeValue); switch (newShareType) { case PUBLIC_LINK: @@ -474,38 +453,20 @@ public class FileContentProvider extends ContentProvider { // verify only for those requests that are not internal final int uriMatch = mUriMatcher.match(uri); - String tableName; - switch (uriMatch) { - case ROOT_DIRECTORY: - case DIRECTORY: - case SINGLE_FILE: + String tableName = switch (uriMatch) { + case ROOT_DIRECTORY, DIRECTORY, SINGLE_FILE -> { VerificationUtils.verifyWhere(selection); // prevent injection in public paths - tableName = ProviderTableMeta.FILE_TABLE_NAME; - break; - case SHARES: - tableName = ProviderTableMeta.OCSHARES_TABLE_NAME; - break; - case CAPABILITIES: - tableName = ProviderTableMeta.CAPABILITIES_TABLE_NAME; - break; - case UPLOADS: - tableName = ProviderTableMeta.UPLOADS_TABLE_NAME; - break; - case SYNCED_FOLDERS: - tableName = ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME; - break; - case EXTERNAL_LINKS: - tableName = ProviderTableMeta.EXTERNAL_LINKS_TABLE_NAME; - break; - case VIRTUAL: - tableName = ProviderTableMeta.VIRTUAL_TABLE_NAME; - break; - case FILESYSTEM: - tableName = ProviderTableMeta.FILESYSTEM_TABLE_NAME; - break; - default: - throw new IllegalArgumentException("Unknown uri id: " + uri); - } + yield ProviderTableMeta.FILE_TABLE_NAME; + } + case SHARES -> ProviderTableMeta.OCSHARES_TABLE_NAME; + case CAPABILITIES -> ProviderTableMeta.CAPABILITIES_TABLE_NAME; + case UPLOADS -> ProviderTableMeta.UPLOADS_TABLE_NAME; + case SYNCED_FOLDERS -> ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME; + case EXTERNAL_LINKS -> ProviderTableMeta.EXTERNAL_LINKS_TABLE_NAME; + case VIRTUAL -> ProviderTableMeta.VIRTUAL_TABLE_NAME; + case FILESYSTEM -> ProviderTableMeta.FILESYSTEM_TABLE_NAME; + default -> throw new IllegalArgumentException("Unknown uri id: " + uri); + }; SupportSQLiteQueryBuilder queryBuilder = SupportSQLiteQueryBuilder.builder(tableName); @@ -520,32 +481,17 @@ public class FileContentProvider extends ContentProvider { String order; if (TextUtils.isEmpty(sortOrder)) { - switch (uriMatch) { - case SHARES: - order = ProviderTableMeta.OCSHARES_DEFAULT_SORT_ORDER; - break; - case CAPABILITIES: - order = ProviderTableMeta.CAPABILITIES_DEFAULT_SORT_ORDER; - break; - case UPLOADS: - order = ProviderTableMeta.UPLOADS_DEFAULT_SORT_ORDER; - break; - case SYNCED_FOLDERS: - order = ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH; - break; - case EXTERNAL_LINKS: - order = ProviderTableMeta.EXTERNAL_LINKS_NAME; - break; - case VIRTUAL: - order = ProviderTableMeta.VIRTUAL_TYPE; - break; - default: // Files - order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER; - break; - case FILESYSTEM: - order = ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH; - break; - } + order = switch (uriMatch) { + case SHARES -> ProviderTableMeta.OCSHARES_DEFAULT_SORT_ORDER; + case CAPABILITIES -> ProviderTableMeta.CAPABILITIES_DEFAULT_SORT_ORDER; + case UPLOADS -> ProviderTableMeta.UPLOADS_DEFAULT_SORT_ORDER; + case SYNCED_FOLDERS -> ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH; + case EXTERNAL_LINKS -> ProviderTableMeta.EXTERNAL_LINKS_NAME; + case VIRTUAL -> ProviderTableMeta.VIRTUAL_TYPE; + case FILESYSTEM -> ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH; + default -> // Files + ProviderTableMeta.FILE_DEFAULT_SORT_ORDER; + }; } else { if (uriMatch == ROOT_DIRECTORY || uriMatch == SINGLE_FILE || uriMatch == DIRECTORY) { VerificationUtils.verifySortOrder(sortOrder); @@ -614,22 +560,21 @@ public class FileContentProvider extends ContentProvider { VerificationUtils.verifyWhere(selection); } - switch (mUriMatcher.match(uri)) { - case DIRECTORY: - return 0; - case SHARES: - return db.update(ProviderTableMeta.OCSHARES_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); - case CAPABILITIES: - return db.update(ProviderTableMeta.CAPABILITIES_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); - case UPLOADS: - return db.update(ProviderTableMeta.UPLOADS_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); - case SYNCED_FOLDERS: - return db.update(ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); - case FILESYSTEM: - return db.update(ProviderTableMeta.FILESYSTEM_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); - default: - return db.update(ProviderTableMeta.FILE_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); - } + return switch (mUriMatcher.match(uri)) { + case DIRECTORY -> 0; + case SHARES -> + db.update(ProviderTableMeta.OCSHARES_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); + case CAPABILITIES -> + db.update(ProviderTableMeta.CAPABILITIES_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); + case UPLOADS -> + db.update(ProviderTableMeta.UPLOADS_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); + case SYNCED_FOLDERS -> + db.update(ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); + case FILESYSTEM -> + db.update(ProviderTableMeta.FILESYSTEM_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); + default -> + db.update(ProviderTableMeta.FILE_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs); + }; } @NonNull @@ -657,23 +602,13 @@ public class FileContentProvider extends ContentProvider { } private boolean isCallerNotAllowed(Uri uri) { - switch (mUriMatcher.match(uri)) { - case SHARES: - case CAPABILITIES: - case UPLOADS: - case SYNCED_FOLDERS: - case EXTERNAL_LINKS: - case VIRTUAL: - case FILESYSTEM: + return switch (mUriMatcher.match(uri)) { + case SHARES, CAPABILITIES, UPLOADS, SYNCED_FOLDERS, EXTERNAL_LINKS, VIRTUAL, FILESYSTEM -> { String callingPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid()); - return callingPackage == null || !callingPackage.equals(mContext.getPackageName()); - - case ROOT_DIRECTORY: - case SINGLE_FILE: - case DIRECTORY: - default: - return false; - } + yield callingPackage == null || !callingPackage.equals(mContext.getPackageName()); + } + default -> false; + }; } diff --git a/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchConfig.kt b/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchConfig.kt index 776d549..dc38546 100644 --- a/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchConfig.kt +++ b/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchConfig.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.providers diff --git a/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java b/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java index cb27855..ef8df0f 100644 --- a/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java +++ b/app/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2016 Juan Carlos González Cabrero * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.providers; @@ -100,6 +100,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider { private String DATA_GROUP; private String DATA_ROOM; private String DATA_REMOTE; + private String DATA_REMOTE_GROUP; private String DATA_EMAIL; private String DATA_CIRCLE; @@ -142,6 +143,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider { DATA_GROUP = AUTHORITY + ".data.group"; DATA_ROOM = AUTHORITY + ".data.room"; DATA_REMOTE = AUTHORITY + ".data.remote"; + DATA_REMOTE_GROUP = AUTHORITY + ".data.remote_group"; DATA_EMAIL = AUTHORITY + ".data.email"; DATA_CIRCLE = AUTHORITY + ".data.circle"; @@ -149,6 +151,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider { sShareTypes.put(DATA_GROUP, ShareType.GROUP); sShareTypes.put(DATA_ROOM, ShareType.ROOM); sShareTypes.put(DATA_REMOTE, ShareType.FEDERATED); + sShareTypes.put(DATA_REMOTE_GROUP, ShareType.FEDERATED_GROUP); sShareTypes.put(DATA_EMAIL, ShareType.EMAIL); sShareTypes.put(DATA_CIRCLE, ShareType.CIRCLE); @@ -228,6 +231,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider { Uri groupBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_GROUP).build(); Uri roomBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_ROOM).build(); Uri remoteBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_REMOTE).build(); + Uri remoteGroupBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_REMOTE_GROUP).build(); Uri emailBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_EMAIL).build(); Uri circleBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_CIRCLE).build(); @@ -282,7 +286,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider { case FEDERATED: if (federatedShareAllowed) { - icon = R.drawable.ic_user; + icon = R.drawable.ic_user_outline; dataUri = Uri.withAppendedPath(remoteBaseUri, shareWith); if (userName.equals(shareWith)) { @@ -297,6 +301,24 @@ public class UsersAndGroupsSearchProvider extends ContentProvider { } break; + case FEDERATED_GROUP: + if (federatedShareAllowed) { + icon = R.drawable.ic_group; + dataUri = Uri.withAppendedPath(remoteGroupBaseUri, shareWith); + + if (userName.equals(shareWith)) { + displayName = name; + subline = getContext().getString(R.string.remote); + subline = ""; + } else { + String[] uriSplitted = shareWith.split("@"); + displayName = name; + subline = getContext().getString(R.string.share_known_remote_on_clarification, + uriSplitted[uriSplitted.length - 1]); + } + } + break; + case USER: displayName = userName; subline = (status.getMessage() == null || status.getMessage().isEmpty()) ? null : diff --git a/app/src/main/java/com/owncloud/android/services/AccountManagerService.java b/app/src/main/java/com/owncloud/android/services/AccountManagerService.java index 6b4245e..05d7b54 100644 --- a/app/src/main/java/com/owncloud/android/services/AccountManagerService.java +++ b/app/src/main/java/com/owncloud/android/services/AccountManagerService.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017 David Luhmer - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.services; diff --git a/app/src/main/java/com/owncloud/android/services/OperationsService.java b/app/src/main/java/com/owncloud/android/services/OperationsService.java index c2b0610..6923606 100644 --- a/app/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/app/src/main/java/com/owncloud/android/services/OperationsService.java @@ -8,7 +8,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2014 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.services; @@ -29,6 +29,7 @@ import android.util.Pair; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.common.NextcloudClient; import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -55,6 +56,7 @@ import com.owncloud.android.operations.GetServerInfoOperation; import com.owncloud.android.operations.MoveFileOperation; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; +import com.owncloud.android.operations.SetFilesDownloadLimitOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareOperation; @@ -64,7 +66,6 @@ import com.owncloud.android.operations.UpdateSharePermissionsOperation; import com.owncloud.android.operations.UpdateShareViaLinkOperation; import java.io.IOException; -import java.util.Iterator; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -81,6 +82,7 @@ public class OperationsService extends Service { private static final String TAG = OperationsService.class.getSimpleName(); public static final String EXTRA_ACCOUNT = "ACCOUNT"; + public static final String EXTRA_POST_DIALOG_EVENT = "EXTRA_POST_DIALOG_EVENT"; public static final String EXTRA_SERVER_URL = "SERVER_URL"; public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; public static final String EXTRA_NEWNAME = "NEWNAME"; @@ -97,8 +99,11 @@ public class OperationsService extends Service { public static final String EXTRA_SHARE_PUBLIC_LABEL = "SHARE_PUBLIC_LABEL"; public static final String EXTRA_SHARE_HIDE_FILE_DOWNLOAD = "HIDE_FILE_DOWNLOAD"; public static final String EXTRA_SHARE_ID = "SHARE_ID"; + public static final String EXTRA_SHARE_REMOTE_ID = "SHARE_REMOTE_ID"; public static final String EXTRA_SHARE_NOTE = "SHARE_NOTE"; public static final String EXTRA_IN_BACKGROUND = "IN_BACKGROUND"; + public static final String EXTRA_FILES_DOWNLOAD_LIMIT = "FILES_DOWNLOAD_LIMIT"; + public static final String EXTRA_SHARE_ATTRIBUTES = "SHARE_ATTRIBUTES"; public static final String ACTION_CREATE_SHARE_VIA_LINK = "CREATE_SHARE_VIA_LINK"; public static final String ACTION_CREATE_SECURE_FILE_DROP = "CREATE_SECURE_FILE_DROP"; @@ -119,6 +124,7 @@ public class OperationsService extends Service { public static final String ACTION_COPY_FILE = "COPY_FILE"; public static final String ACTION_CHECK_CURRENT_CREDENTIALS = "CHECK_CURRENT_CREDENTIALS"; public static final String ACTION_RESTORE_VERSION = "RESTORE_VERSION"; + public static final String ACTION_UPDATE_FILES_DOWNLOAD_LIMIT = "UPDATE_FILES_DOWNLOAD_LIMIT"; private ServiceHandler mOperationsHandler; private OperationsServiceBinder mOperationsBinder; @@ -260,7 +266,7 @@ public class OperationsService extends Service { */ private final ConcurrentMap mBoundListeners = new ConcurrentHashMap<>(); - private ServiceHandler mServiceHandler; + private final ServiceHandler mServiceHandler; public OperationsServiceBinder(ServiceHandler serviceHandler) { mServiceHandler = serviceHandler; @@ -380,7 +386,7 @@ public class OperationsService extends Service { OperationsService mService; - private ConcurrentLinkedQueue> mPendingOperations = + private final ConcurrentLinkedQueue> mPendingOperations = new ConcurrentLinkedQueue<>(); private RemoteOperation mCurrentOperation; private Target mLastTarget; @@ -416,11 +422,12 @@ public class OperationsService extends Service { if (next != null) { mCurrentOperation = next.second; RemoteOperationResult result; + OwnCloudAccount ocAccount = null; + try { /// prepare client object to send the request to the ownCloud server if (mLastTarget == null || !mLastTarget.equals(next.first)) { mLastTarget = next.first; - OwnCloudAccount ocAccount; if (mLastTarget.mAccount != null) { ocAccount = new OwnCloudAccount(mLastTarget.mAccount, mService); } else { @@ -430,9 +437,26 @@ public class OperationsService extends Service { getClientFor(ocAccount, mService); } - /// perform the operation - result = mCurrentOperation.execute(mOwnCloudClient); - } catch (AccountsException e) { + // perform the operation + try { + result = mCurrentOperation.execute(mOwnCloudClient); + if (!result.isSuccess()) { + final var code = "code: " + result.getCode(); + final var httpCode = "HTTP_CODE: " + result.getHttpCode(); + Log_OC.e(TAG,"Operation failed " + code + httpCode); + } + } catch (UnsupportedOperationException e) { + // TODO remove - added to aid in transition to NextcloudClient + + if (ocAccount == null) { + throw e; + } + + NextcloudClient nextcloudClient = OwnCloudClientManagerFactory.getDefaultSingleton() + .getNextcloudClientFor(ocAccount, mService.getBaseContext()); + result = mCurrentOperation.run(nextcloudClient); + } + } catch (AccountsException | IOException e) { if (mLastTarget.mAccount == null) { Log_OC.e(TAG, "Error while trying to get authorization for a NULL account", e); @@ -442,15 +466,6 @@ public class OperationsService extends Service { } result = new RemoteOperationResult(e); - } catch (IOException e) { - if (mLastTarget.mAccount == null) { - Log_OC.e(TAG, "Error while trying to get authorization for a NULL account", - e); - } else { - Log_OC.e(TAG, "Error while trying to get authorization for " + - mLastTarget.mAccount.name, e); - } - result = new RemoteOperationResult(e); } catch (Exception e) { if (mLastTarget.mAccount == null) { Log_OC.e(TAG, "Unexpected error for a NULL account", e); @@ -537,10 +552,6 @@ public class OperationsService extends Service { false); updateLinkOperation.setHideFileDownload(hideFileDownload); -// if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_UPLOAD)) { -// updateLinkOperation.setPublicUpload(true); -// } - if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_LABEL)) { updateLinkOperation.setLabel(operationIntent.getStringExtra(EXTRA_SHARE_PUBLIC_LABEL)); } @@ -590,6 +601,8 @@ public class OperationsService extends Service { .getLongExtra(EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, 0L); boolean hideFileDownload = operationIntent.getBooleanExtra(EXTRA_SHARE_HIDE_FILE_DOWNLOAD, false); + String attributes = operationIntent.getStringExtra(EXTRA_SHARE_ATTRIBUTES); + if (!TextUtils.isEmpty(remotePath)) { CreateShareWithShareeOperation createShareWithShareeOperation = new CreateShareWithShareeOperation(remotePath, @@ -600,6 +613,7 @@ public class OperationsService extends Service { sharePassword, expirationDateInMillis, hideFileDownload, + attributes, fileDataStorageManager, getApplicationContext(), user, @@ -614,9 +628,11 @@ public class OperationsService extends Service { case ACTION_UPDATE_SHARE_INFO: shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1); + long shareRemoteId = operationIntent.getLongExtra(EXTRA_SHARE_REMOTE_ID, -1); - if (shareId > 0) { + if (shareId > 0 || shareRemoteId > 0) { UpdateShareInfoOperation updateShare = new UpdateShareInfoOperation(shareId, + shareRemoteId, fileDataStorageManager); int permissionsToChange = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, -1); @@ -638,6 +654,9 @@ public class OperationsService extends Service { updateShare.setLabel(operationIntent.getStringExtra(EXTRA_SHARE_PUBLIC_LABEL)); } + String shareAttributes = operationIntent.getStringExtra(EXTRA_SHARE_ATTRIBUTES); + updateShare.setAttributes(shareAttributes); + operation = updateShare; } break; @@ -672,6 +691,11 @@ public class OperationsService extends Service { case ACTION_REMOVE: // Remove file or folder OCFile file = IntentExtensionsKt.getParcelableArgument(operationIntent, EXTRA_FILE, OCFile.class); + if (file == null) { + Log_OC.w(TAG, "file is null cannot remove file"); + break; + } + boolean onlyLocalCopy = operationIntent.getBooleanExtra(EXTRA_REMOVE_ONLY_LOCAL, false); boolean inBackground = operationIntent.getBooleanExtra(EXTRA_IN_BACKGROUND, false); operation = new RemoveFileOperation(file, @@ -692,12 +716,15 @@ public class OperationsService extends Service { case ACTION_SYNC_FILE: remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); + boolean postDialogEvent = operationIntent.getBooleanExtra(EXTRA_POST_DIALOG_EVENT, true); boolean syncFileContents = operationIntent.getBooleanExtra(EXTRA_SYNC_FILE_CONTENTS, true); operation = new SynchronizeFileOperation(remotePath, user, syncFileContents, getApplicationContext(), - fileDataStorageManager); + fileDataStorageManager, + false, + postDialogEvent); break; case ACTION_SYNC_FOLDER: @@ -706,8 +733,8 @@ public class OperationsService extends Service { this, // TODO remove this dependency from construction time remotePath, user, - System.currentTimeMillis(), // TODO remove this dependency from construction time - fileDataStorageManager + fileDataStorageManager, + false ); break; @@ -729,10 +756,24 @@ public class OperationsService extends Service { case ACTION_RESTORE_VERSION: FileVersion fileVersion = IntentExtensionsKt.getParcelableArgument(operationIntent, EXTRA_FILE_VERSION, FileVersion.class); + if (fileVersion == null) { + Log_OC.w(TAG, "file version is null cannot restore file"); + break; + } + operation = new RestoreFileVersionRemoteOperation(fileVersion.getLocalId(), fileVersion.getFileName()); break; + case ACTION_UPDATE_FILES_DOWNLOAD_LIMIT: + shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1); + int newLimit = operationIntent.getIntExtra(EXTRA_FILES_DOWNLOAD_LIMIT, -1); + + if (shareId > 0) { + operation = new SetFilesDownloadLimitOperation(shareId, newLimit, fileDataStorageManager, getApplicationContext()); + } + break; + default: // do nothing break; diff --git a/app/src/main/java/com/owncloud/android/services/SyncFolderHandler.java b/app/src/main/java/com/owncloud/android/services/SyncFolderHandler.java index 917b79a..4c8d75b 100644 --- a/app/src/main/java/com/owncloud/android/services/SyncFolderHandler.java +++ b/app/src/main/java/com/owncloud/android/services/SyncFolderHandler.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.services; diff --git a/app/src/main/java/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java b/app/src/main/java/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java index 3956b84..42258de 100644 --- a/app/src/main/java/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java +++ b/app/src/main/java/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java @@ -9,7 +9,7 @@ * SPDX-FileCopyrightText: 2015 David A. Velasco * SPDX-FileCopyrightText: 2011-2012 Bartosz Przybylski * SPDX-FileCopyrightText: 2011 Sven Aßmann - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.syncadapter; diff --git a/app/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java b/app/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java index 1dc10c9..5a8b609 100644 --- a/app/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/app/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java @@ -9,7 +9,7 @@ * SPDX-FileCopyrightText: 2013-2015 David A. Velasco * SPDX-FileCopyrightText: 2011-2012 Bartosz Przybylski * SPDX-FileCopyrightText: 2011 Sven Aßmann - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.syncadapter; @@ -153,7 +153,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { mLastFailedResult = null; mConflictsFound = 0; mFailsInFavouritesFound = 0; - mForgottenLocalFiles = new HashMap(); + mForgottenLocalFiles = new HashMap<>(); mSyncResult = syncResult; mSyncResult.fullSyncRequested = false; mSyncResult.delayUntil = (System.currentTimeMillis()/1000) + 3*60*60; // avoid too many automatic synchronizations @@ -481,10 +481,8 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { /// includes a pending intent in the notification showing a more detailed explanation Intent explanationIntent = new Intent(getContext(), ErrorsWhileCopyingHandlerActivity.class); explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_USER, getUser()); - ArrayList remotePaths = new ArrayList(); - ArrayList localPaths = new ArrayList(); - remotePaths.addAll(mForgottenLocalFiles.keySet()); - localPaths.addAll(mForgottenLocalFiles.values()); + ArrayList remotePaths = new ArrayList<>(mForgottenLocalFiles.keySet()); + ArrayList localPaths = new ArrayList<>(mForgottenLocalFiles.values()); explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_LOCAL_PATHS, localPaths); explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_REMOTE_PATHS, remotePaths); explanationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -525,11 +523,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { private void showNotification(int id, NotificationCompat.Builder builder) { NotificationManager notificationManager = (NotificationManager) getContext(). getSystemService(Context.NOTIFICATION_SERVICE); - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - builder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_FILE_SYNC); - } - + builder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_FILE_SYNC); notificationManager.notify(id, builder.build()); } /** diff --git a/app/src/main/java/com/owncloud/android/syncadapter/FileSyncService.java b/app/src/main/java/com/owncloud/android/syncadapter/FileSyncService.java index 030c6c3..50dcfe8 100644 --- a/app/src/main/java/com/owncloud/android/syncadapter/FileSyncService.java +++ b/app/src/main/java/com/owncloud/android/syncadapter/FileSyncService.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2013 David A. Velasco * SPDX-FileCopyrightText: 2011-2012 Bartosz Przybylski * SPDX-FileCopyrightText: 2011 Sven Aßmann - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.syncadapter; diff --git a/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.java b/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.java deleted file mode 100644 index 5485b55..0000000 --- a/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Andy Scherzinger - * @author Stefan Niedermann - * Copyright (C) 2021 Andy Scherzinger - * Copyright (C) 2021 Stefan Niedermann - * - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.owncloud.android.ui; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.RelativeLayout; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.target.BitmapImageViewTarget; -import com.nextcloud.client.account.User; -import com.owncloud.android.R; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.shares.ShareeUser; -import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.theme.ViewThemeUtils; - -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.annotation.Px; -import androidx.core.content.ContextCompat; -import androidx.core.content.res.ResourcesCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.core.graphics.drawable.RoundedBitmapDrawable; -import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; - -public class AvatarGroupLayout extends RelativeLayout implements DisplayUtils.AvatarGenerationListener { - private static final String TAG = AvatarGroupLayout.class.getSimpleName(); - - private final static int MAX_AVATAR_COUNT = 3; - - private final Drawable borderDrawable; - @Px private final int avatarSize; - @Px private final int avatarBorderSize; - @Px private final int overlapPx; - - public AvatarGroupLayout(Context context) { - this(context, null); - } - - public AvatarGroupLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public AvatarGroupLayout(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public AvatarGroupLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - avatarBorderSize = DisplayUtils.convertDpToPixel(2, context); - avatarSize = DisplayUtils.convertDpToPixel(40, context); - overlapPx = DisplayUtils.convertDpToPixel(24, context); - borderDrawable = ContextCompat.getDrawable(context, R.drawable.round_bgnd); - assert borderDrawable != null; - DrawableCompat.setTint(borderDrawable, ContextCompat.getColor(context, R.color.bg_default)); - } - - public void setAvatars(@NonNull User user, - @NonNull List sharees, - final ViewThemeUtils viewThemeUtils) { - @NonNull Context context = getContext(); - removeAllViews(); - RelativeLayout.LayoutParams avatarLayoutParams; - int avatarCount; - int shareeSize = Math.min(sharees.size(), MAX_AVATAR_COUNT); - - Resources resources = context.getResources(); - float avatarRadius = resources.getDimension(R.dimen.list_item_avatar_icon_radius); - ShareeUser sharee; - - for (avatarCount = 0; avatarCount < shareeSize; avatarCount++) { - avatarLayoutParams = new RelativeLayout.LayoutParams(avatarSize, avatarSize); - avatarLayoutParams.setMargins(0, 0, avatarCount * overlapPx, 0); - avatarLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - - final ImageView avatar = new ImageView(context); - avatar.setLayoutParams(avatarLayoutParams); - avatar.setPadding(avatarBorderSize, avatarBorderSize, avatarBorderSize, avatarBorderSize); - - avatar.setBackground(borderDrawable); - addView(avatar); - avatar.requestLayout(); - - if (avatarCount == 0 && sharees.size() > MAX_AVATAR_COUNT) { - avatar.setImageResource(R.drawable.ic_people); - viewThemeUtils.platform.tintTextDrawable(context, avatar.getDrawable()); - } else { - sharee = sharees.get(avatarCount); - switch (sharee.getShareType()) { - case GROUP: - case EMAIL: - case ROOM: - case CIRCLE: - viewThemeUtils.files.createAvatar(sharee.getShareType(), avatar, context); - break; - case FEDERATED: - showFederatedShareAvatar(context, - sharee.getUserId(), - avatarRadius, - resources, - avatar, - viewThemeUtils); - break; - default: - avatar.setTag(sharee); - DisplayUtils.setAvatar(user, - sharee.getUserId(), - sharee.getDisplayName(), - this, - avatarRadius, - resources, - avatar, - context); - break; - } - } - } - - // Recalculate container size based on avatar count - int size = overlapPx * (avatarCount - 1) + avatarSize; - ViewGroup.LayoutParams rememberParam = getLayoutParams(); - rememberParam.width = size; - setLayoutParams(rememberParam); - } - - private void showFederatedShareAvatar(Context context, - String user, - float avatarRadius, - Resources resources, - ImageView avatar, - ViewThemeUtils viewThemeUtils) { - // maybe federated share - String[] split = user.split("@"); - String userId = split[0]; - String server = split[1]; - - String url = "https://" + server + "/index.php/avatar/" + userId + "/" + - resources.getInteger(R.integer.file_avatar_px); - - Drawable placeholder; - try { - placeholder = TextDrawable.createAvatarByUserId(userId, avatarRadius); - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - placeholder = viewThemeUtils.platform.colorDrawable(ResourcesCompat.getDrawable(resources, - R.drawable.account_circle_white, - null), - ContextCompat.getColor(context, R.color.black)); - } - - avatar.setTag(null); - Glide.with(context).load(url) - .asBitmap() - .placeholder(placeholder) - .error(placeholder) - .into(new BitmapImageViewTarget(avatar) { - @Override - protected void setResource(Bitmap resource) { - RoundedBitmapDrawable circularBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, - resource); - circularBitmapDrawable.setCircular(true); - avatar.setImageDrawable(circularBitmapDrawable); - } - }); - } - - @Override - public void avatarGenerated(Drawable avatarDrawable, Object callContext) { - ((ImageView) callContext).setImageDrawable(avatarDrawable); - } - - @Override - public boolean shouldCallGeneratedCallback(String tag, Object callContext) { - return ((ImageView) callContext).getTag().equals(tag); - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt b/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt new file mode 100644 index 0000000..ab22b2c --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt @@ -0,0 +1,182 @@ +/* + * Nextcloud Android client application + * + * @author Andy Scherzinger + * @author Stefan Niedermann + * Copyright (C) 2021 Andy Scherzinger + * Copyright (C) 2021 Stefan Niedermann + * + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui + +import android.content.Context +import android.content.res.Resources +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.widget.ImageView +import android.widget.RelativeLayout +import androidx.annotation.Px +import androidx.core.content.ContextCompat +import androidx.core.content.res.ResourcesCompat +import androidx.core.graphics.drawable.DrawableCompat +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.nextcloud.client.account.User +import com.nextcloud.utils.GlideHelper.loadCircularBitmapIntoImageView +import com.owncloud.android.R +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.lib.resources.shares.ShareeUser +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener +import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlin.math.min + +@Suppress("MagicNumber") +class AvatarGroupLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : RelativeLayout(context, attrs, defStyleAttr, defStyleRes), + AvatarGenerationListener { + private val borderDrawable = ContextCompat.getDrawable(context, R.drawable.round_bgnd) + + @Px + private val avatarSize: Int = DisplayUtils.convertDpToPixel(40f, context) + + @Px + private val avatarBorderSize: Int = DisplayUtils.convertDpToPixel(2f, context) + + @Px + private val overlapPx: Int = DisplayUtils.convertDpToPixel(24f, context) + + init { + checkNotNull(borderDrawable) + DrawableCompat.setTint(borderDrawable, ContextCompat.getColor(context, R.color.bg_default)) + } + + @Suppress("LongMethod", "TooGenericExceptionCaught") + fun setAvatars(user: User, sharees: MutableList, viewThemeUtils: ViewThemeUtils) { + val context = getContext() + removeAllViews() + var avatarLayoutParams: LayoutParams? + val shareeSize = min(sharees.size, MAX_AVATAR_COUNT) + val resources = context.resources + val avatarRadius = resources.getDimension(R.dimen.list_item_avatar_icon_radius) + var sharee: ShareeUser + + var avatarCount = 0 + while (avatarCount < shareeSize) { + avatarLayoutParams = LayoutParams(avatarSize, avatarSize).apply { + setMargins(0, 0, avatarCount * overlapPx, 0) + addRule(ALIGN_PARENT_RIGHT) + } + + val avatar = ImageView(context).apply { + layoutParams = avatarLayoutParams + setPadding(avatarBorderSize, avatarBorderSize, avatarBorderSize, avatarBorderSize) + background = borderDrawable + } + + addView(avatar) + avatar.requestLayout() + + if (avatarCount == 0 && sharees.size > MAX_AVATAR_COUNT) { + avatar.setImageResource(R.drawable.ic_people) + viewThemeUtils.platform.tintDrawable(context, avatar.drawable, ColorRole.ON_SURFACE) + } else { + sharee = sharees[avatarCount] + when (sharee.shareType) { + ShareType.GROUP, ShareType.EMAIL, ShareType.ROOM, ShareType.CIRCLE -> + viewThemeUtils.files.createAvatar( + sharee.shareType, + avatar, + context + ) + + ShareType.FEDERATED, ShareType.FEDERATED_GROUP -> showFederatedShareAvatar( + context, + sharee.userId!!, + avatarRadius, + resources, + avatar, + viewThemeUtils + ) + + else -> { + avatar.tag = sharee + DisplayUtils.setAvatar( + user, + sharee.userId!!, + sharee.displayName, + this, + avatarRadius, + resources, + avatar, + context + ) + } + } + } + avatarCount++ + } + + // Recalculate container size based on avatar count + val size = overlapPx * (avatarCount - 1) + avatarSize + val rememberParam = layoutParams + rememberParam.width = size + layoutParams = rememberParam + } + + @Suppress("TooGenericExceptionCaught") + private fun showFederatedShareAvatar( + context: Context, + user: String, + avatarRadius: Float, + resources: Resources, + avatar: ImageView, + viewThemeUtils: ViewThemeUtils + ) { + val split = user.split("@") + val userId = split.getOrNull(0) ?: user + val server = split.getOrNull(1) + + val url = if (server != null) { + "https://$server/index.php/avatar/$userId/${resources.getInteger(R.integer.file_avatar_px)}" + } else { + // fallback: no federated server, maybe use local avatar + null + } + + val placeholder: Drawable = try { + TextDrawable.createAvatarByUserId(userId, avatarRadius) + } catch (e: Exception) { + Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e) + viewThemeUtils.platform.colorDrawable( + ResourcesCompat + .getDrawable(resources, R.drawable.account_circle_white, null)!!, + ContextCompat.getColor(context, R.color.black) + ) + } + + avatar.tag = null + if (url != null) { + loadCircularBitmapIntoImageView(context, url, avatar, placeholder) + } else { + avatar.setImageDrawable(placeholder) + } + } + + override fun avatarGenerated(avatarDrawable: Drawable?, callContext: Any) { + (callContext as ImageView).setImageDrawable(avatarDrawable) + } + + override fun shouldCallGeneratedCallback(tag: String?, callContext: Any): Boolean = + (callContext as ImageView).tag == tag + + companion object { + private val TAG: String = AvatarGroupLayout::class.java.simpleName + private const val MAX_AVATAR_COUNT = 3 + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/CompletionCallback.kt b/app/src/main/java/com/owncloud/android/ui/CompletionCallback.kt new file mode 100644 index 0000000..3bb0a84 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/CompletionCallback.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui + +interface CompletionCallback { + fun onComplete(value: Boolean) +} diff --git a/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java b/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java index 0d73e63..7c1ce64 100644 --- a/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java +++ b/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui; diff --git a/app/src/main/java/com/owncloud/android/ui/ListPreferenceDialog.kt b/app/src/main/java/com/owncloud/android/ui/ListPreferenceDialog.kt new file mode 100644 index 0000000..6cacb9b --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/ListPreferenceDialog.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui + +import android.app.AlertDialog +import android.app.Dialog +import android.content.Context +import android.preference.ListPreference +import android.util.AttributeSet +import com.nextcloud.utils.extensions.setVisibleIf + +@Suppress("DEPRECATION") +class ListPreferenceDialog(context: Context?, attrs: AttributeSet?) : ListPreference(context, attrs) { + + fun showDialog() { + if (!isDialogCreated()) { + onClick() + } + } + + fun dismissible(value: Boolean) { + if (isDialogCreated()) { + dialog.setCancelable(value) + dialog.setCanceledOnTouchOutside(value) + } + } + + fun enableCancelButton(value: Boolean) { + if (isDialogCreated()) { + (dialog as? AlertDialog)?.let { + val cancelButton = it.getButton(Dialog.BUTTON_NEGATIVE) + cancelButton?.setVisibleIf(value) + cancelButton?.isEnabled = value + } + } + } + + private fun isDialogCreated(): Boolean = dialog != null +} diff --git a/app/src/main/java/com/owncloud/android/ui/NextcloudWebViewClient.kt b/app/src/main/java/com/owncloud/android/ui/NextcloudWebViewClient.kt index dbb7282..3e65201 100644 --- a/app/src/main/java/com/owncloud/android/ui/NextcloudWebViewClient.kt +++ b/app/src/main/java/com/owncloud/android/ui/NextcloudWebViewClient.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Elv1zz * SPDX-FileCopyrightText: 2022 Unpublished - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui diff --git a/app/src/main/java/com/owncloud/android/ui/SquareImageView.java b/app/src/main/java/com/owncloud/android/ui/SquareImageView.java index 778ae3c..c4b2a1d 100644 --- a/app/src/main/java/com/owncloud/android/ui/SquareImageView.java +++ b/app/src/main/java/com/owncloud/android/ui/SquareImageView.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2014-2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui; diff --git a/app/src/main/java/com/owncloud/android/ui/SquareLinearLayout.java b/app/src/main/java/com/owncloud/android/ui/SquareLinearLayout.java index 6345317..bca3b38 100644 --- a/app/src/main/java/com/owncloud/android/ui/SquareLinearLayout.java +++ b/app/src/main/java/com/owncloud/android/ui/SquareLinearLayout.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2014 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui; diff --git a/app/src/main/java/com/owncloud/android/ui/StatusDrawable.java b/app/src/main/java/com/owncloud/android/ui/StatusDrawable.java index a81d7e2..129811f 100644 --- a/app/src/main/java/com/owncloud/android/ui/StatusDrawable.java +++ b/app/src/main/java/com/owncloud/android/ui/StatusDrawable.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui; @@ -35,8 +35,6 @@ public class StatusDrawable extends Drawable { private Paint backgroundPaint; private final float radius; private Context context; - private final static int whiteBackground = Color.argb(200, 255, 255, 255); - private final static int onlineStatus = Color.argb(255, 73, 179, 130); public StatusDrawable(Status status, float statusSize, Context context) { backgroundPaint = new Paint(); @@ -46,21 +44,24 @@ public class StatusDrawable extends Drawable { radius = statusSize; if (TextUtils.isEmpty(status.getIcon())) { + this.context = context; + backgroundPaint.setColor(context.getColor(R.color.bg_default)); + switch (status.getStatus()) { case DND: icon = R.drawable.ic_user_status_dnd; - backgroundPaint.setColor(whiteBackground); - this.context = context; + break; + + case BUSY: + icon = R.drawable.ic_user_status_busy; break; case ONLINE: - backgroundPaint.setColor(onlineStatus); + icon = R.drawable.ic_user_status_online; break; case AWAY: icon = R.drawable.ic_user_status_away; - backgroundPaint.setColor(whiteBackground); - this.context = context; break; default: @@ -71,7 +72,7 @@ public class StatusDrawable extends Drawable { } else { text = status.getIcon(); - backgroundPaint.setColor(whiteBackground); + backgroundPaint = null; textPaint = new Paint(); textPaint.setColor(Color.WHITE); diff --git a/app/src/main/java/com/owncloud/android/ui/TextDrawable.java b/app/src/main/java/com/owncloud/android/ui/TextDrawable.java index c017a01..10321ef 100644 --- a/app/src/main/java/com/owncloud/android/ui/TextDrawable.java +++ b/app/src/main/java/com/owncloud/android/ui/TextDrawable.java @@ -5,7 +5,7 @@ * SPDX-FileCopyrightText: 2019-2021 Chris Narkiewicz * SPDX-FileCopyrightText: 2015-2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2016-2018 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui; diff --git a/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java b/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java index 1ddddb9..7fad98a 100644 --- a/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java +++ b/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui; @@ -54,9 +54,7 @@ public class ThemeableSwitchPreference extends SwitchPreference { for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); - if (child instanceof Switch) { - Switch switchView = (Switch) child; - + if (child instanceof Switch switchView) { viewThemeUtils.platform.colorSwitch(switchView); break; diff --git a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java index e5bef49..6c10373 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities; @@ -13,6 +13,7 @@ import android.view.View; import com.google.android.material.snackbar.Snackbar; import com.nextcloud.client.network.ClientFactory; +import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.common.NextcloudClient; import com.owncloud.android.R; import com.owncloud.android.databinding.ActivityListLayoutBinding; @@ -29,7 +30,6 @@ import com.owncloud.android.ui.interfaces.ActivityListInterface; import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.theme.ViewThemeUtils; import java.util.List; @@ -51,7 +51,7 @@ public class ActivitiesActivity extends DrawerActivity implements ActivityListIn ActivityListLayoutBinding binding; private ActivityListAdapter adapter; - private int lastGiven; + private long lastGiven; private boolean isLoadingActivities; private ActivitiesContract.ActionListener actionListener; private Snackbar snackbar; @@ -59,7 +59,7 @@ public class ActivitiesActivity extends DrawerActivity implements ActivityListIn @Inject ActivitiesRepository activitiesRepository; @Inject FilesRepository filesRepository; @Inject ClientFactory clientFactory; - @Inject ViewThemeUtils viewThemeUtils; + @Inject ConnectivityService connectivityService; @Override protected void onCreate(Bundle savedInstanceState) { @@ -77,7 +77,7 @@ public class ActivitiesActivity extends DrawerActivity implements ActivityListIn viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingList); // setup drawer - setupDrawer(R.id.nav_activity); + setupDrawer(); updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_activities)); binding.swipeContainingList.setOnRefreshListener(() -> { @@ -153,11 +153,7 @@ public class ActivitiesActivity extends DrawerActivity implements ActivityListIn @Override protected void onResume() { super.onResume(); - actionListener.onResume(); - - setDrawerMenuItemChecked(R.id.nav_activity); - setupContent(); } @@ -168,11 +164,8 @@ public class ActivitiesActivity extends DrawerActivity implements ActivityListIn } @Override - public void showActivities(List activities, NextcloudClient client, int lastGiven) { - boolean clear = false; - if (this.lastGiven == ActivitiesContract.ActionListener.UNDEFINED) { - clear = true; - } + public void showActivities(List activities, NextcloudClient client, long lastGiven) { + boolean clear = this.lastGiven == ActivitiesContract.ActionListener.UNDEFINED; adapter.setActivityItems(activities, client, clear); this.lastGiven = lastGiven; @@ -190,7 +183,16 @@ public class ActivitiesActivity extends DrawerActivity implements ActivityListIn @Override public void showActivitiesLoadError(String error) { - snackbar = DisplayUtils.showSnackMessage(this, error); + connectivityService.isNetworkAndServerAvailable(result -> { + if (result) { + snackbar = DisplayUtils.showSnackMessage(this, error); + } else { + showEmptyContent(getString(R.string.server_not_reachable), + getString(R.string.server_not_reachable_content)); + binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_cloud_sync_off); + } + }); + } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesContract.java b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesContract.java index 4a0bd19..b5396d4 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesContract.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesContract.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities; @@ -15,7 +15,7 @@ import java.util.List; public interface ActivitiesContract { interface View { - void showActivities(List activities, NextcloudClient client, int lastGiven); + void showActivities(List activities, NextcloudClient client, long lastGiven); void showActivitiesLoadError(String error); void showActivityDetailUI(OCFile ocFile); void showActivityDetailUIIsNull(); @@ -28,7 +28,7 @@ public interface ActivitiesContract { interface ActionListener { int UNDEFINED = -1; - void loadActivities(int lastGiven); + void loadActivities(long lastGiven); void openActivity(String fileUrl, BaseActivity baseActivity); diff --git a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesPresenter.java b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesPresenter.java index e39ec74..1c4ff09 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesPresenter.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/ActivitiesPresenter.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities; @@ -34,7 +34,7 @@ public class ActivitiesPresenter implements ActivitiesContract.ActionListener { } @Override - public void loadActivities(int lastGiven) { + public void loadActivities(long lastGiven) { if (UNDEFINED == lastGiven) { activitiesView.showLoadingMessage(); } else { @@ -42,7 +42,7 @@ public class ActivitiesPresenter implements ActivitiesContract.ActionListener { } activitiesRepository.getActivities(lastGiven, new ActivitiesRepository.LoadActivitiesCallback() { @Override - public void onActivitiesLoaded(List activities, NextcloudClient client, int lastGiven) { + public void onActivitiesLoaded(List activities, NextcloudClient client, long lastGiven) { if (!activityStopped) { activitiesView.setProgressIndicatorState(false); diff --git a/app/src/main/java/com/owncloud/android/ui/activities/StickyHeaderItemDecoration.java b/app/src/main/java/com/owncloud/android/ui/activities/StickyHeaderItemDecoration.java index ee6ac9d..f862db5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/StickyHeaderItemDecoration.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/StickyHeaderItemDecoration.java @@ -5,7 +5,7 @@ * Copyright (C) 2019 Sevastyan Savanyuk * Copyright (C) 2019 Nextcloud GmbH * - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities; diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesRepository.java b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesRepository.java index 688a90d..e23c3a3 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesRepository.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesRepository.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.activities; @@ -17,9 +17,9 @@ import androidx.annotation.NonNull; */ public interface ActivitiesRepository { interface LoadActivitiesCallback { - void onActivitiesLoaded(List activities, NextcloudClient client, int lastGiven); + void onActivitiesLoaded(List activities, NextcloudClient client, long lastGiven); void onActivitiesLoadedError(String error); } - void getActivities(int lastGiven, @NonNull LoadActivitiesCallback callback); + void getActivities(long lastGiven, @NonNull LoadActivitiesCallback callback); } diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApi.java b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApi.java index 1b561b3..870416c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApi.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApi.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.activities; @@ -19,10 +19,10 @@ import java.util.List; public interface ActivitiesServiceApi { interface ActivitiesServiceCallback { - void onLoaded(T activities, NextcloudClient client, int lastGiven); + void onLoaded(T activities, NextcloudClient client, long lastGiven); void onError (String error); } - void getAllActivities(int lastGiven, ActivitiesServiceApi.ActivitiesServiceCallback> callback); + void getAllActivities(long lastGiven, ActivitiesServiceApi.ActivitiesServiceCallback> callback); } diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java index 4e81beb..0534b6f 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java @@ -1,9 +1,11 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.activities; @@ -42,7 +44,7 @@ public class ActivitiesServiceApiImpl implements ActivitiesServiceApi { } @Override - public void getAllActivities(int lastGiven, ActivitiesServiceCallback> callback) { + public void getAllActivities(long lastGiven, ActivitiesServiceCallback> callback) { GetActivityListTask getActivityListTask = new GetActivityListTask(accountManager.getUser(), lastGiven, callback); @@ -54,12 +56,12 @@ public class ActivitiesServiceApiImpl implements ActivitiesServiceApi { private final ActivitiesServiceCallback> callback; private List activities; private final User user; - private int lastGiven; + private long lastGiven; private String errorMessage; private NextcloudClient client; private GetActivityListTask(User user, - int lastGiven, + long lastGiven, ActivitiesServiceCallback> callback) { this.user = user; this.lastGiven = lastGiven; @@ -89,12 +91,12 @@ public class ActivitiesServiceApiImpl implements ActivitiesServiceApi { final ArrayList data = result.getData(); activities = (ArrayList) data.get(0); - lastGiven = (int) data.get(1); + lastGiven = (long) data.get(1); return Boolean.TRUE; } else { Log_OC.d(TAG, result.getLogMessage()); // show error - errorMessage = result.getLogMessage(); + errorMessage = result.getLogMessage(MainApp.getAppContext()); if (result.getHttpCode() == HttpStatus.SC_NOT_MODIFIED) { errorMessage = context.getString(R.string.file_list_empty_headline_server_search); } diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivityRepositories.java b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivityRepositories.java index b393f66..b4bfeb5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivityRepositories.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivityRepositories.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.activities; diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/RemoteActivitiesRepository.java b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/RemoteActivitiesRepository.java index 8812a6f..82adde6 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/activities/RemoteActivitiesRepository.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/activities/RemoteActivitiesRepository.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.activities; @@ -22,18 +22,18 @@ public class RemoteActivitiesRepository implements ActivitiesRepository { @Override - public void getActivities(int lastGiven, @NonNull LoadActivitiesCallback callback) { + public void getActivities(long lastGiven, @NonNull LoadActivitiesCallback callback) { activitiesServiceApi.getAllActivities(lastGiven, - new ActivitiesServiceApi.ActivitiesServiceCallback>() { - @Override - public void onLoaded(List activities, NextcloudClient client, int lastGiven) { - callback.onActivitiesLoaded(activities, client, lastGiven); - } + new ActivitiesServiceApi.ActivitiesServiceCallback<>() { + @Override + public void onLoaded(List activities, NextcloudClient client, long lastGiven) { + callback.onActivitiesLoaded(activities, client, lastGiven); + } - @Override - public void onError(String error) { - callback.onActivitiesLoadedError(error); - } - }); + @Override + public void onError(String error) { + callback.onActivitiesLoadedError(error); + } + }); } } diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FileRepositories.java b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FileRepositories.java index 4f90cbe..37da9e9 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FileRepositories.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FileRepositories.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.files; diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesRepository.java b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesRepository.java index beaed7b..49300a4 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesRepository.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesRepository.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.files; diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApi.java b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApi.java index e06a3b7..5d08e56 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApi.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApi.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.files; diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java index b569bf8..d775bad 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.files; diff --git a/app/src/main/java/com/owncloud/android/ui/activities/data/files/RemoteFilesRepository.java b/app/src/main/java/com/owncloud/android/ui/activities/data/files/RemoteFilesRepository.java index 1ec23f6..759e0b4 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/data/files/RemoteFilesRepository.java +++ b/app/src/main/java/com/owncloud/android/ui/activities/data/files/RemoteFilesRepository.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activities.data.files; @@ -22,16 +22,16 @@ public class RemoteFilesRepository implements FilesRepository { @Override public void readRemoteFile(String path, BaseActivity activity, @NonNull ReadRemoteFileCallback callback) { - filesServiceApi.readRemoteFile(path, activity, new FilesServiceApi.FilesServiceCallback() { - @Override - public void onLoaded(OCFile ocFile) { - callback.onFileLoaded(ocFile); - } + filesServiceApi.readRemoteFile(path, activity, new FilesServiceApi.FilesServiceCallback<>() { + @Override + public void onLoaded(OCFile ocFile) { + callback.onFileLoaded(ocFile); + } - @Override - public void onError(String error) { - callback.onFileLoadError(error); - } - }); + @Override + public void onError(String error) { + callback.onFileLoadError(error); + } + }); } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java index 7a57b67..82a0598 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java @@ -2,7 +2,8 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity; @@ -10,6 +11,7 @@ import android.accounts.Account; import android.content.Intent; import android.os.Bundle; +import com.nextcloud.android.common.ui.util.extensions.AppCompatActivityExtensionsKt; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; @@ -17,6 +19,8 @@ import com.nextcloud.client.mixins.MixinRegistry; import com.nextcloud.client.mixins.SessionMixin; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.DarkMode; +import com.nextcloud.repository.ClientRepository; +import com.nextcloud.repository.RemoteClientRepository; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.utils.Log_OC; @@ -43,14 +47,14 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab private boolean paused; protected boolean enableAccountHandling = true; - private MixinRegistry mixinRegistry = new MixinRegistry(); + private final MixinRegistry mixinRegistry = new MixinRegistry(); private SessionMixin sessionMixin; @Inject UserAccountManager accountManager; @Inject AppPreferences preferences; @Inject FileDataStorageManager fileDataStorageManager; - private AppPreferences.Listener onPreferencesChanged = new AppPreferences.Listener() { + private final AppPreferences.Listener onPreferencesChanged = new AppPreferences.Listener() { @Override public void onDarkThemeModeChanged(DarkMode mode) { onThemeSettingsModeChanged(); @@ -61,8 +65,11 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab return accountManager; } + private ClientRepository clientRepository; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { + AppCompatActivityExtensionsKt.applyEdgeToEdgeWithSystemBarPadding(this); super.onCreate(savedInstanceState); sessionMixin = new SessionMixin(this, accountManager); mixinRegistry.add(sessionMixin); @@ -70,6 +77,8 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab if (enableAccountHandling) { mixinRegistry.onCreate(savedInstanceState); } + + clientRepository = new RemoteClientRepository(accountManager.getUser(), this, this); } @Override @@ -115,7 +124,9 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab protected void onRestart() { Log_OC.v(TAG, "onRestart() start"); super.onRestart(); - mixinRegistry.onRestart(); + if (enableAccountHandling) { + mixinRegistry.onRestart(); + } } private void onThemeSettingsModeChanged() { @@ -175,4 +186,9 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab public FileDataStorageManager getStorageManager() { return fileDataStorageManager; } + + public ClientRepository getClientRepository() { + return clientRepository; + } + } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt new file mode 100644 index 0000000..8d13d53 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.activity + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.nextcloud.ui.ChooseStorageLocationDialogFragment + +class ChooseStorageLocationActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val chooseStorageLocationDialogFragment = ChooseStorageLocationDialogFragment.newInstance() + supportFragmentManager.setFragmentResultListener( + KEY_RESULT_STORAGE_LOCATION, + this + ) { _, result -> + setResult( + ChooseStorageLocationDialogFragment.STORAGE_LOCATION_RESULT_CODE, + Intent().putExtra( + KEY_RESULT_STORAGE_LOCATION, + result.getString(KEY_RESULT_STORAGE_LOCATION) + ) + ) + } + chooseStorageLocationDialogFragment.show(supportFragmentManager, "choose_storage_location") + } + + companion object { + const val KEY_RESULT_STORAGE_LOCATION = ChooseStorageLocationDialogFragment.KEY_RESULT_STORAGE_LOCATION + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt index 78eafe2..308d7eb 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt @@ -1,11 +1,11 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Tobias Kaminsky * SPDX-FileCopyrightText: 2016 Nextcloud - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity @@ -30,7 +30,7 @@ open class CommunityActivity : DrawerActivity() { setupToolbar() updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_community)) - setupDrawer(R.id.nav_community) + setupDrawer() binding.communityReleaseCandidateText.movementMethod = LinkMovementMethod.getInstance() setupContributeForumView() setupContributeTranslationView() @@ -125,9 +125,4 @@ open class CommunityActivity : DrawerActivity() { } return retval } - - override fun onResume() { - super.onResume() - setDrawerMenuItemChecked(R.id.nav_community) - } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java b/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java index 05ebeae..7ce91df 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java @@ -1,12 +1,12 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2014 David A. Velasco * SPDX-FileCopyrightText: 2012 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.ui.activity; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt index a83e5c6..fc5f579 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt @@ -2,27 +2,36 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2024 Jonas Mayer - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2019 Alice Gaudon * SPDX-FileCopyrightText: 2012 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.ui.activity +import android.annotation.SuppressLint +import android.app.NotificationManager import android.content.Context import android.content.Intent import android.os.Bundle import android.widget.Toast +import androidx.fragment.app.FragmentTransaction +import androidx.lifecycle.lifecycleScope import com.nextcloud.client.account.User +import com.nextcloud.client.database.entity.OfflineOperationEntity import com.nextcloud.client.jobs.download.FileDownloadHelper +import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsNotificationManager +import com.nextcloud.client.jobs.operation.FileOperationHelper import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.upload.UploadNotificationManager import com.nextcloud.model.HTTPStatusCodes +import com.nextcloud.utils.extensions.getDecryptedPath import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.extensions.logFileSize import com.owncloud.android.R -import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.ThumbnailsCacheManager import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.db.OCUpload import com.owncloud.android.files.services.NameCollisionPolicy @@ -32,25 +41,32 @@ import com.owncloud.android.lib.resources.files.model.RemoteFile import com.owncloud.android.ui.dialog.ConflictsResolveDialog import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener +import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FileStorageUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject /** * Wrapper activity which will be launched if keep-in-sync file will be modified by external application. */ -class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener { - @JvmField +@Suppress("TooManyFunctions") +class ConflictsResolveActivity : + FileActivity(), + OnConflictDecisionMadeListener { @Inject - var uploadsStorageManager: UploadsStorageManager? = null + lateinit var uploadsStorageManager: UploadsStorageManager - @JvmField @Inject - var fileStorageManager: FileDataStorageManager? = null + lateinit var fileOperationHelper: FileOperationHelper private var conflictUploadId: Long = 0 + private var offlineOperationPath: String? = null private var existingFile: OCFile? = null private var newFile: OCFile? = null private var localBehaviour = FileUploadWorker.LOCAL_BEHAVIOUR_FORGET + private lateinit var offlineOperationNotificationManager: OfflineOperationsNotificationManager @JvmField var listener: OnConflictDecisionMadeListener? = null @@ -60,7 +76,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener getArguments(savedInstanceState) - val upload = uploadsStorageManager?.getUploadById(conflictUploadId) + val upload = uploadsStorageManager.getUploadById(conflictUploadId) if (upload != null) { localBehaviour = upload.localAction } @@ -68,6 +84,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener // new file was modified locally in file system newFile = file setupOnConflictDecisionMadeListener(upload) + offlineOperationNotificationManager = OfflineOperationsNotificationManager(this, viewThemeUtils) } private fun getArguments(savedInstanceState: Bundle?) { @@ -75,7 +92,9 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener conflictUploadId = savedInstanceState.getLong(EXTRA_CONFLICT_UPLOAD_ID) existingFile = savedInstanceState.getParcelableArgument(EXTRA_EXISTING_FILE, OCFile::class.java) localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR) + offlineOperationPath = savedInstanceState.getString(EXTRA_OFFLINE_OPERATION_PATH) } else { + offlineOperationPath = intent.getStringExtra(EXTRA_OFFLINE_OPERATION_PATH) conflictUploadId = intent.getLongExtra(EXTRA_CONFLICT_UPLOAD_ID, -1) existingFile = intent.getParcelableArgument(EXTRA_EXISTING_FILE, OCFile::class.java) localBehaviour = intent.getIntExtra(EXTRA_LOCAL_BEHAVIOUR, localBehaviour) @@ -84,93 +103,204 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener private fun setupOnConflictDecisionMadeListener(upload: OCUpload?) { listener = OnConflictDecisionMadeListener { decision: Decision? -> - val file = newFile // local file got changed, so either upload it or replace it again by server + + // local file got changed, so either upload it or replace it again by server + val file = newFile + // version val user = user.orElseThrow { RuntimeException() } - when (decision) { - Decision.CANCEL -> {} - Decision.KEEP_LOCAL -> { - upload?.let { - FileUploadHelper.instance().removeFileUpload(it.remotePath, it.accountName) - } - FileUploadHelper.instance().uploadUpdatedFile( - user, - arrayOf(file), - localBehaviour, - NameCollisionPolicy.OVERWRITE - ) - } - Decision.KEEP_BOTH -> { - upload?.let { - FileUploadHelper.instance().removeFileUpload(it.remotePath, it.accountName) - } - FileUploadHelper.instance().uploadUpdatedFile( - user, - arrayOf(file), - localBehaviour, - NameCollisionPolicy.RENAME - ) - } - - Decision.KEEP_SERVER -> { - if (!shouldDeleteLocal()) { - // Overwrite local file - file?.let { - FileDownloadHelper.instance().downloadFile( - getUser().orElseThrow { RuntimeException() }, - file, - conflictUploadId = conflictUploadId - ) - } - } - - upload?.let { - FileUploadHelper.instance().removeFileUpload(it.remotePath, it.accountName) - - UploadNotificationManager( - applicationContext, - viewThemeUtils - ).dismissOldErrorNotification(it.remotePath, it.localPath) - } - } - - else -> {} + val offlineOperation = if (offlineOperationPath != null) { + fileDataStorageManager.offlineOperationDao.getByPath(offlineOperationPath!!) + } else { + null } + + when (decision) { + Decision.KEEP_LOCAL -> keepLocal(file, upload, user) + Decision.KEEP_BOTH -> keepBoth(file, upload, user) + Decision.KEEP_SERVER -> keepServer(file, upload) + Decision.KEEP_OFFLINE_FOLDER -> keepOfflineFolder(file, offlineOperation) + Decision.KEEP_SERVER_FOLDER -> keepServerFile(offlineOperation) + Decision.KEEP_BOTH_FOLDER -> keepBothFolder(offlineOperation, file) + else -> Unit + } + + upload?.remotePath?.let { oldFilePath -> + val oldFile = storageManager.getFileByDecryptedRemotePath(oldFilePath) + updateThumbnailIfNeeded(decision, file, oldFile) + } + + dismissConflictResolveNotification(file) finish() } } + private fun updateThumbnailIfNeeded(decision: Decision?, file: OCFile?, oldFile: OCFile?) { + if (decision == Decision.KEEP_BOTH || decision == Decision.KEEP_LOCAL) { + // When the user chooses to replace the remote file with the new local file, + // remove the old file's thumbnail so a new one can be generated + if (decision == Decision.KEEP_LOCAL) { + ThumbnailsCacheManager.removeFromCache(oldFile) + } + + file?.isUpdateThumbnailNeeded = true + fileDataStorageManager.saveFile(file) + } + } + + private fun dismissConflictResolveNotification(file: OCFile?) { + file ?: return + + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + val tag = NotificationUtils.createUploadNotificationTag(file) + notificationManager.cancel(tag, FileUploadWorker.NOTIFICATION_ERROR_ID) + } + + private fun keepBothFolder(offlineOperation: OfflineOperationEntity?, serverFile: OCFile?) { + offlineOperation ?: return + fileDataStorageManager.keepOfflineOperationAndServerFile(offlineOperation, serverFile) + backgroundJobManager.startOfflineOperations() + offlineOperationNotificationManager.dismissNotification(offlineOperation.id) + } + + private fun keepServerFile(offlineOperation: OfflineOperationEntity?) { + offlineOperation ?: return + fileDataStorageManager.offlineOperationDao.delete(offlineOperation) + + val id = offlineOperation.id ?: return + offlineOperationNotificationManager.dismissNotification(id) + } + + private fun keepOfflineFolder(serverFile: OCFile?, offlineOperation: OfflineOperationEntity?) { + serverFile ?: return + offlineOperation ?: return + + lifecycleScope.launch(Dispatchers.IO) { + val client = clientRepository.getOwncloudClient() ?: return@launch + val isSuccess = fileOperationHelper.removeFile( + serverFile, + onlyLocalCopy = false, + inBackground = false, + client = client + ) + + if (isSuccess) { + backgroundJobManager.startOfflineOperations() + withContext(Dispatchers.Main) { + offlineOperationNotificationManager.dismissNotification(offlineOperation.id) + } + } + } + } + + private fun keepLocal(file: OCFile?, upload: OCUpload?, user: User) { + upload?.let { + FileUploadHelper.instance().removeFileUpload(it.remotePath, it.accountName) + } + + FileUploadHelper.instance().uploadUpdatedFile( + user, + arrayOf(file), + localBehaviour, + NameCollisionPolicy.OVERWRITE + ) + } + + private fun keepBoth(file: OCFile?, upload: OCUpload?, user: User) { + upload?.let { + FileUploadHelper.instance().removeFileUpload(it.remotePath, it.accountName) + } + + FileUploadHelper.instance().uploadUpdatedFile( + user, + arrayOf(file), + localBehaviour, + NameCollisionPolicy.RENAME + ) + } + + private fun keepServer(file: OCFile?, upload: OCUpload?) { + if (!shouldDeleteLocal()) { + // Overwrite local file + file?.let { + FileDownloadHelper.instance().downloadFile( + user.orElseThrow { RuntimeException() }, + file, + conflictUploadId = conflictUploadId + ) + } + } + + upload?.let { + FileUploadHelper.instance().removeFileUpload(it.remotePath, it.accountName) + + UploadNotificationManager( + applicationContext, + viewThemeUtils, + upload.uploadId.toInt() + ).dismissOldErrorNotification(it.remotePath, it.localPath) + } + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putLong(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId) - outState.putParcelable(EXTRA_EXISTING_FILE, existingFile) - outState.putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour) + existingFile.logFileSize(TAG) + + outState.run { + putLong(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId) + putParcelable(EXTRA_EXISTING_FILE, existingFile) + putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour) + } } - override fun conflictDecisionMade(decision: Decision) { + override fun conflictDecisionMade(decision: Decision?) { listener?.conflictDecisionMade(decision) } + @Suppress("ReturnCount") override fun onStart() { super.onStart() + if (account == null) { finish() return } + if (newFile == null) { Log_OC.e(TAG, "No file received") finish() return } + + offlineOperationPath?.let { path -> + newFile?.let { ocFile -> + val offlineOperation = fileDataStorageManager.offlineOperationDao.getByPath(path) + + if (offlineOperation == null) { + showErrorAndFinish() + return + } + + val (ft, _) = prepareDialog() + val dialog = ConflictsResolveDialog.newInstance( + context = this, + leftFile = offlineOperation, + rightFile = ocFile + ) + dialog.show(ft, "conflictDialog") + return + } + } + if (existingFile == null) { - val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(newFile) ?: return + val remotePath = fileDataStorageManager.retrieveRemotePathConsideringEncryption(newFile) ?: return val operation = ReadFileRemoteOperation(remotePath) @Suppress("TooGenericExceptionCaught") - Thread { + lifecycleScope.launch(Dispatchers.IO) { try { - val result = operation.execute(account, this) + val result = operation.execute(account, this@ConflictsResolveActivity) if (result.isSuccess) { existingFile = FileStorageUtils.fillOCFile(result.data[0] as RemoteFile) existingFile?.lastSyncDateForProperties = System.currentTimeMillis() @@ -183,14 +313,15 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener Log_OC.e(TAG, "Error when trying to fetch remote file", e) showErrorAndFinish() } - }.start() + } } else { - val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(existingFile) ?: return + val remotePath = fileDataStorageManager.retrieveRemotePathConsideringEncryption(existingFile) ?: return startDialog(remotePath) } } - private fun startDialog(remotePath: String) { + @SuppressLint("CommitTransaction") + private fun prepareDialog(): Pair { val userOptional = user if (!userOptional.isPresent) { Log_OC.e(TAG, "User not present") @@ -203,13 +334,22 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener if (prev != null) { fragmentTransaction.remove(prev) } - if (existingFile != null && storageManager.fileExists(remotePath)) { + + return fragmentTransaction to user.get() + } + + private fun startDialog(remotePath: String) { + val (ft, user) = prepareDialog() + + if (existingFile != null && storageManager.fileExists(remotePath) && newFile != null) { val dialog = ConflictsResolveDialog.newInstance( - existingFile, - newFile, - userOptional.get() + title = storageManager.getDecryptedPath(existingFile!!), + context = this, + leftFile = newFile!!, + rightFile = existingFile!!, + user = user ) - dialog.show(fragmentTransaction, "conflictDialog") + dialog.show(ft, "conflictDialog") } else { // Account was changed to a different one - just finish Log_OC.e(TAG, "Account was changed, finishing") @@ -219,26 +359,22 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener private fun showErrorAndFinish(code: Int? = null) { val message = parseErrorMessage(code) - runOnUiThread { - Toast.makeText(this, message, Toast.LENGTH_LONG).show() + lifecycleScope.launch(Dispatchers.Main) { + Toast.makeText(this@ConflictsResolveActivity, message, Toast.LENGTH_LONG).show() finish() } } - private fun parseErrorMessage(code: Int?): String { - return if (code == HTTPStatusCodes.NOT_FOUND.code) { - getString(R.string.uploader_file_not_found_on_server_message) - } else { - getString(R.string.conflict_dialog_error) - } + private fun parseErrorMessage(code: Int?): String = if (code == HTTPStatusCodes.NOT_FOUND.code) { + getString(R.string.uploader_file_not_found_on_server_message) + } else { + getString(R.string.conflict_dialog_error) } /** * @return whether the local version of the files is to be deleted. */ - private fun shouldDeleteLocal(): Boolean { - return localBehaviour == FileUploadWorker.LOCAL_BEHAVIOUR_DELETE - } + private fun shouldDeleteLocal(): Boolean = localBehaviour == FileUploadWorker.LOCAL_BEHAVIOUR_DELETE companion object { /** @@ -251,24 +387,26 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener */ const val EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR" const val EXTRA_EXISTING_FILE = "EXISTING_FILE" + private const val EXTRA_OFFLINE_OPERATION_PATH = "EXTRA_OFFLINE_OPERATION_PATH" + private val TAG = ConflictsResolveActivity::class.java.simpleName @JvmStatic - fun createIntent( - file: OCFile?, - user: User?, - conflictUploadId: Long, - flag: Int?, - context: Context? - ): Intent { - val intent = Intent(context, ConflictsResolveActivity::class.java) - if (flag != null) { - intent.flags = intent.flags or flag + fun createIntent(file: OCFile?, user: User?, conflictUploadId: Long, flag: Int?, context: Context?): Intent = + Intent(context, ConflictsResolveActivity::class.java).apply { + if (flag != null) { + flags = flags or flag + } + putExtra(EXTRA_FILE, file) + putExtra(EXTRA_USER, user) + putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId) + } + + @JvmStatic + fun createIntent(file: OCFile, offlineOperationPath: String, context: Context): Intent = + Intent(context, ConflictsResolveActivity::class.java).apply { + putExtra(EXTRA_FILE, file) + putExtra(EXTRA_OFFLINE_OPERATION_PATH, offlineOperationPath) } - intent.putExtra(EXTRA_FILE, file) - intent.putExtra(EXTRA_USER, user) - intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId) - return intent - } } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java index 42fc3fc..0811c5d 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt index 2fdd965..2811b20 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt @@ -1,8 +1,8 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 13d6230..29aa804 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -1,14 +1,14 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2021 TSI-mc + * SPDX-FileCopyrightText: 2021-2024 TSI-mc * SPDX-FileCopyrightText: 2020 Infomaniak Network SA * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Nextcloud * SPDX-FileCopyrightText: 2016 ownCloud Inc. - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.ui.activity; @@ -20,12 +20,14 @@ import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.PictureDrawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -39,18 +41,17 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import com.bumptech.glide.GenericRequestBuilder; -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.StreamEncoder; -import com.bumptech.glide.load.resource.file.FileToStreamDecoder; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.target.Target; +import com.bumptech.glide.request.transition.Transition; +import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.button.MaterialButton; import com.google.android.material.navigation.NavigationView; import com.google.android.material.progressindicator.LinearProgressIndicator; import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.files.DeepLinkConstants; +import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.client.preferences.AppPreferences; @@ -58,6 +59,11 @@ import com.nextcloud.common.NextcloudClient; import com.nextcloud.ui.ChooseAccountDialogFragment; import com.nextcloud.ui.composeActivity.ComposeActivity; import com.nextcloud.ui.composeActivity.ComposeDestination; +import com.nextcloud.utils.GlideHelper; +import com.nextcloud.utils.LinkHelper; +import com.nextcloud.utils.extensions.ActivityExtensionsKt; +import com.nextcloud.utils.extensions.ViewExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.PassCodeManager; @@ -92,12 +98,9 @@ import com.owncloud.android.ui.preview.PreviewTextStringFragment; import com.owncloud.android.ui.trashbin.TrashbinActivity; import com.owncloud.android.utils.BitmapUtils; import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.DrawableUtil; import com.owncloud.android.utils.DrawerMenuUtil; import com.owncloud.android.utils.FilesSyncHelper; -import com.owncloud.android.utils.svg.MenuSimpleTarget; -import com.owncloud.android.utils.svg.SVGorImage; -import com.owncloud.android.utils.svg.SvgOrImageBitmapTranscoder; -import com.owncloud.android.utils.svg.SvgOrImageDecoder; import com.owncloud.android.utils.theme.CapabilityUtils; import org.greenrobot.eventbus.EventBus; @@ -105,7 +108,6 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -113,14 +115,19 @@ import java.util.Optional; import javax.inject.Inject; +import androidx.annotation.IdRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hct.Hct; +import kotlin.Unit; /** * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback @@ -131,7 +138,6 @@ public abstract class DrawerActivity extends ToolbarActivity private static final String TAG = DrawerActivity.class.getSimpleName(); private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE"; - private static final String KEY_CHECKED_MENU_ITEM = "CHECKED_MENU_ITEM"; private static final int ACTION_MANAGE_ACCOUNTS = 101; private static final int MENU_ORDER_EXTERNAL_LINKS = 3; private static final int MENU_ITEM_EXTERNAL_LINK = 111; @@ -151,7 +157,7 @@ public abstract class DrawerActivity extends ToolbarActivity /** * Reference to the navigation view. */ - private NavigationView mNavigationView; + private NavigationView drawerNavigationView; /** * Reference to the navigation view header. @@ -166,7 +172,7 @@ public abstract class DrawerActivity extends ToolbarActivity /** * Id of the checked menu item. */ - private int mCheckedMenuItem = Menu.NONE; + public static int menuItemId = Menu.NONE; /** * container layout of the quota view. @@ -192,37 +198,28 @@ public abstract class DrawerActivity extends ToolbarActivity private ExternalLinksProvider externalLinksProvider; private ArbitraryDataProvider arbitraryDataProvider; + private BottomNavigationView bottomNavigationView; + @Inject AppPreferences preferences; @Inject ClientFactory clientFactory; - /** - * Initializes the drawer, its content and highlights the menu item with the given id. This method needs to be - * called after the content view has been set. - * - * @param menuItemId the menu item to be checked/highlighted - */ - protected void setupDrawer(int menuItemId) { - setupDrawer(); - setDrawerMenuItemChecked(menuItemId); - } - /** * Initializes the drawer and its content. This method needs to be called after the content view has been set. */ protected void setupDrawer() { mDrawerLayout = findViewById(R.id.drawer_layout); - mNavigationView = findViewById(R.id.nav_view); - if (mNavigationView != null) { + drawerNavigationView = findViewById(R.id.nav_view); + if (drawerNavigationView != null) { // Setting up drawer header - mNavigationViewHeader = mNavigationView.getHeaderView(0); + mNavigationViewHeader = drawerNavigationView.getHeaderView(0); updateHeader(); - setupDrawerMenu(mNavigationView); + setupDrawerMenu(drawerNavigationView); getAndDisplayUserQuota(); setupQuotaElement(); } @@ -232,6 +229,86 @@ public abstract class DrawerActivity extends ToolbarActivity if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } + + bottomNavigationView = findViewById(R.id.bottom_navigation); + if (bottomNavigationView != null) { + themeBottomNavigationMenu(); + checkAssistantBottomNavigationMenu(); + handleBottomNavigationViewClicks(); + } + + setNavigationViewItemChecked(); + } + + private void themeBottomNavigationMenu() { + viewThemeUtils.platform.colorBottomNavigationView(bottomNavigationView); + } + + @SuppressFBWarnings("RV") + private void checkAssistantBottomNavigationMenu() { + boolean isAssistantAvailable = getCapabilities().getAssistant().isTrue(); + + bottomNavigationView + .getMenu() + .findItem(R.id.nav_assistant) + .setVisible(isAssistantAvailable); + } + + @SuppressFBWarnings("RV") + private void handleBottomNavigationViewClicks() { + bottomNavigationView.setOnItemSelectedListener(menuItem -> { + menuItemId = menuItem.getItemId(); + + exitSelectionMode(); + resetOnlyPersonalAndOnDevice(); + + if (menuItemId == R.id.nav_all_files) { + showFiles(false,false); + if (this instanceof FileDisplayActivity fda) { + fda.browseToRoot(); + } + EventBus.getDefault().post(new ChangeMenuEvent()); + } else if (menuItemId == R.id.nav_favorites) { + setupToolbar(); + handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH), menuItemId); + } else if (menuItemId == R.id.nav_assistant && !(this instanceof ComposeActivity)) { + startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title); + } else if (menuItemId == R.id.nav_gallery) { + setupToolbar(); + startPhotoSearch(menuItem.getItemId()); + } + + // Remove extra icon from the action bar + if (getSupportActionBar() != null) { + getSupportActionBar().setIcon(null); + } + + setNavigationViewItemChecked(); + + return false; + }); + } + + @Nullable + public OCFileListFragment getOCFileListFragment() { + Fragment fragment = ActivityExtensionsKt.lastFragment(this); + if (fragment instanceof OCFileListFragment fileListFragment) { + return fileListFragment; + } + + fragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); + if (fragment instanceof OCFileListFragment fileListFragment) { + return fileListFragment; + } + + return null; + } + + private void exitSelectionMode() { + Fragment fragment = getOCFileListFragment(); + if (fragment instanceof OCFileListFragment fileListFragment) { + fileListFragment.exitSelectionMode(); + } } /** @@ -239,7 +316,6 @@ public abstract class DrawerActivity extends ToolbarActivity */ private void setupDrawerToggle() { mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) { - /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { super.onDrawerClosed(view); @@ -270,7 +346,9 @@ public abstract class DrawerActivity extends ToolbarActivity R.drawable.ic_arrow_back, null); - viewThemeUtils.platform.tintToolbarArrowDrawable(this, mDrawerToggle, backArrow); + if (backArrow != null) { + viewThemeUtils.platform.tintToolbarArrowDrawable(this, mDrawerToggle, backArrow); + } } /** @@ -285,84 +363,105 @@ public abstract class DrawerActivity extends ToolbarActivity } public void updateHeader() { - int primaryColor = themeColorUtils.unchangedPrimaryColor(getAccount(), this); + final var account = getAccount(); boolean isClientBranded = getResources().getBoolean(R.bool.is_branded_client); + final OCCapability capability = getCapabilities(); - if (getAccount() != null && - getCapabilities().getServerBackground() != null && !isClientBranded) { - - OCCapability capability = getCapabilities(); - String logo = capability.getServerLogo(); + if (capability != null && account != null && capability.getServerBackground() != null && !isClientBranded) { + int primaryColor = themeColorUtils.unchangedPrimaryColor(account, this); + String serverLogoURL = capability.getServerLogo(); // set background to primary color LinearLayout drawerHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_view); drawerHeader.setBackgroundColor(primaryColor); - if (!TextUtils.isEmpty(logo) && URLUtil.isValidUrl(logo)) { - // background image - GenericRequestBuilder requestBuilder = Glide.with(this) - .using(Glide.buildStreamModelLoader(Uri.class, this), InputStream.class) - .from(Uri.class) - .as(SVGorImage.class) - .transcode(new SvgOrImageBitmapTranscoder(128, 128), Bitmap.class) - .sourceEncoder(new StreamEncoder()) - .cacheDecoder(new FileToStreamDecoder<>(new SvgOrImageDecoder())) - .decoder(new SvgOrImageDecoder()); - - // background image - SimpleTarget target = new SimpleTarget<>() { - @Override - public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { - - Bitmap logo = resource; - int width = resource.getWidth(); - int height = resource.getHeight(); - int max = Math.max(width, height); - if (max > MAX_LOGO_SIZE_PX) { - logo = BitmapUtils.scaleBitmap(resource, MAX_LOGO_SIZE_PX, width, height, max); - } - - Drawable[] drawables = {new ColorDrawable(primaryColor), - new BitmapDrawable(getResources(), logo)}; - LayerDrawable layerDrawable = new LayerDrawable(drawables); - - String name = capability.getServerName(); - setDrawerHeaderLogo(layerDrawable, name); - } - }; - - requestBuilder - .diskCacheStrategy(DiskCacheStrategy.SOURCE) - .load(Uri.parse(logo)) - .into(target); + if (!TextUtils.isEmpty(serverLogoURL) && URLUtil.isValidUrl(serverLogoURL)) { + Target target = createSVGLogoTarget(primaryColor, capability); + getClientRepository().getNextcloudClient(nextcloudClient -> { + GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this, + nextcloudClient, + serverLogoURL, + target, + R.drawable.background); + return Unit.INSTANCE; + }); } } // hide ecosystem apps according to user preference or in branded client - LinearLayout banner = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps); + ConstraintLayout banner = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps); boolean shouldHideTopBanner = isClientBranded || !preferences.isShowEcosystemApps(); if (shouldHideTopBanner) { hideTopBanner(banner); } else { - showTopBanner(banner, primaryColor); + showTopBanner(banner); } } - private void hideTopBanner(LinearLayout banner) { + private Target createSVGLogoTarget(int primaryColor, OCCapability capability) { + return new CustomTarget<>() { + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + Bitmap bitmap; + + if (resource instanceof PictureDrawable pictureDrawable) { + bitmap = Bitmap.createBitmap( + pictureDrawable.getIntrinsicWidth(), + pictureDrawable.getIntrinsicHeight(), + Bitmap.Config.ARGB_8888); + + Canvas canvas = new Canvas(bitmap); + canvas.drawPicture(pictureDrawable.getPicture()); + + } else if (resource instanceof BitmapDrawable bitmapDrawable) { + bitmap = bitmapDrawable.getBitmap(); + } else { + Log_OC.e(TAG, "Unsupported drawable type: " + resource.getClass().getName()); + return; + } + + // Scale down if necessary + Bitmap logo = bitmap; + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + int max = Math.max(width, height); + if (max > MAX_LOGO_SIZE_PX) { + logo = BitmapUtils.scaleBitmap(bitmap, MAX_LOGO_SIZE_PX, width, height, max); + } + + Drawable[] drawables = { + new ColorDrawable(primaryColor), + new BitmapDrawable(getResources(), logo) + }; + LayerDrawable layerDrawable = new LayerDrawable(drawables); + + String name = capability.getServerName(); + setDrawerHeaderLogo(layerDrawable, name); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) {} + }; + } + + private void hideTopBanner(ConstraintLayout banner) { banner.setVisibility(View.GONE); } - private void showTopBanner(LinearLayout banner, int primaryColor) { + private void showTopBanner(ConstraintLayout banner) { LinearLayout notesView = banner.findViewById(R.id.drawer_ecosystem_notes); LinearLayout talkView = banner.findViewById(R.id.drawer_ecosystem_talk); LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more); LinearLayout assistantView = banner.findViewById(R.id.drawer_ecosystem_assistant); - notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); - talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); - moreView.setOnClickListener(v -> openAppStore("Nextcloud", true)); - assistantView.setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1)); + notesView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_NOTES, getUser(), this)); + talkView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_TALK, getUser(), this)); + moreView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppStore("Nextcloud", true, this)); + assistantView.setOnClickListener(v -> { + DrawerActivity.menuItemId = Menu.NONE; + startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title); + }); if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) { assistantView.setVisibility(View.VISIBLE); } else { @@ -372,8 +471,14 @@ public abstract class DrawerActivity extends ToolbarActivity List views = Arrays.asList(notesView, talkView, moreView, assistantView); int iconColor; - if (Hct.fromInt(primaryColor).getTone() < 80.0) { - iconColor = Color.WHITE; + final var account = getAccount(); + if (account != null) { + int primaryColor = themeColorUtils.unchangedPrimaryColor(account, this); + if (Hct.fromInt(primaryColor).getTone() < 80.0) { + iconColor = Color.WHITE; + } else { + iconColor = getColor(R.color.grey_800_transparent); + } } else { iconColor = getColor(R.color.grey_800_transparent); } @@ -390,45 +495,6 @@ public abstract class DrawerActivity extends ToolbarActivity banner.setVisibility(View.VISIBLE); } - /** - * Open specified app and, if not installed redirect to corresponding download. - * - * @param packageName of app to be opened - */ - private void openAppOrStore(String packageName) { - Intent intent = getPackageManager().getLaunchIntentForPackage(packageName); - if (intent != null) { - // app installed - open directly - intent.putExtra(FileDisplayActivity.KEY_ACCOUNT, getUser().get().hashCode()); - startActivity(intent); - } else { - // app not found - open market (Google Play Store, F-Droid, etc.) - openAppStore(packageName, false); - } - } - - /** - * Open app store page of specified app or search for specified string. Will attempt to open browser when no app - * store is available. - * - * @param string packageName or url-encoded search string - * @param search false -> show app corresponding to packageName; true -> open search for string - */ - private void openAppStore(String string, boolean search) { - String suffix = (search ? "search?q=" : "details?id=") + string; - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://" + suffix)); - try { - startActivity(intent); - } catch (android.content.ActivityNotFoundException activityNotFoundException1) { - // all is lost: open google play store web page for app - if (!search) { - suffix = "apps/" + suffix; - } - intent.setData(Uri.parse("https://play.google.com/store/" + suffix)); - startActivity(intent); - } - } - private void setDrawerHeaderLogo(Drawable drawable, String serverName) { ImageView imageHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_logo); imageHeader.setImageDrawable(drawable); @@ -449,7 +515,6 @@ public abstract class DrawerActivity extends ToolbarActivity * @param navigationView the drawers navigation view */ private void setupDrawerMenu(NavigationView navigationView) { - navigationView.setItemIconTintList(null); // setup actions for drawer menu items navigationView.setNavigationItemSelectedListener( @@ -460,7 +525,6 @@ public abstract class DrawerActivity extends ToolbarActivity return true; }); - User account = accountManager.getUser(); filterDrawerMenu(navigationView.getMenu(), account); } @@ -485,57 +549,65 @@ public abstract class DrawerActivity extends ToolbarActivity } private void onNavigationItemClicked(final MenuItem menuItem) { - setDrawerMenuItemChecked(menuItem.getItemId()); - int itemId = menuItem.getItemId(); + menuItemId = itemId; + setNavigationViewItemChecked(); if (itemId == R.id.nav_all_files || itemId == R.id.nav_personal_files) { - if (this instanceof FileDisplayActivity && - !(((FileDisplayActivity) this).getLeftFragment() instanceof GalleryFragment) && - !(((FileDisplayActivity) this).getLeftFragment() instanceof SharedListFragment) && - !(((FileDisplayActivity) this).getLeftFragment() instanceof GroupfolderListFragment) && - !(((FileDisplayActivity) this).getLeftFragment() instanceof PreviewTextStringFragment)) { + if (this instanceof FileDisplayActivity fda && + !(fda.getLeftFragment() instanceof GalleryFragment) && + !(fda.getLeftFragment() instanceof SharedListFragment) && + !(fda.getLeftFragment() instanceof GroupfolderListFragment) && + !(fda.getLeftFragment() instanceof PreviewTextStringFragment)) { showFiles(false, itemId == R.id.nav_personal_files); - ((FileDisplayActivity) this).browseToRoot(); + fda.browseToRoot(); EventBus.getDefault().post(new ChangeMenuEvent()); } else { MainApp.showOnlyFilesOnDevice(false); MainApp.showOnlyPersonalFiles(itemId == R.id.nav_personal_files); Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - - if (this instanceof ComposeActivity) { - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - } - intent.setAction(FileDisplayActivity.ALL_FILES); - intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); } closeDrawer(); } else if (itemId == R.id.nav_favorites) { - handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH), - menuItem.getItemId()); + resetOnlyPersonalAndOnDevice(); + setupToolbar(); + handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH), menuItem.getItemId()); } else if (itemId == R.id.nav_gallery) { + resetOnlyPersonalAndOnDevice(); + setupToolbar(); startPhotoSearch(menuItem.getItemId()); } else if (itemId == R.id.nav_on_device) { EventBus.getDefault().post(new ChangeMenuEvent()); showFiles(true, false); } else if (itemId == R.id.nav_uploads) { + resetOnlyPersonalAndOnDevice(); startActivity(UploadListActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); } else if (itemId == R.id.nav_trashbin) { + resetOnlyPersonalAndOnDevice(); startActivity(TrashbinActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); } else if (itemId == R.id.nav_activity) { + resetOnlyPersonalAndOnDevice(); startActivity(ActivitiesActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); - } else if (itemId == R.id.nav_notifications) { - startActivity(NotificationsActivity.class); } else if (itemId == R.id.nav_settings) { - startActivity(SettingsActivity.class); + resetOnlyPersonalAndOnDevice(); + + /** + * Since pressing the back button in SettingsActivity always returns to the all file list, we can clear the stack. + * {@link SettingsActivity#onBackPressed() + */ + final Intent intent = new Intent(this, SettingsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); } else if (itemId == R.id.nav_community) { + resetOnlyPersonalAndOnDevice(); startActivity(CommunityActivity.class); } else if (itemId == R.id.nav_logout) { - mCheckedMenuItem = -1; + resetOnlyPersonalAndOnDevice(); + menuItemId = Menu.NONE; MenuItem isNewMenuItemChecked = menuItem.setChecked(false); Log_OC.d(TAG,"onNavigationItemClicked nav_logout setChecked " + isNewMenuItemChecked); final Optional optionalUser = getUser(); @@ -543,17 +615,19 @@ public abstract class DrawerActivity extends ToolbarActivity UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager()); } } else if (itemId == R.id.nav_shared) { + resetOnlyPersonalAndOnDevice(); startSharedSearch(menuItem); } else if (itemId == R.id.nav_recently_modified) { + resetOnlyPersonalAndOnDevice(); startRecentlyModifiedSearch(menuItem); } else if (itemId == R.id.nav_assistant) { - startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, itemId); + resetOnlyPersonalAndOnDevice(); + startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title); } else if (itemId == R.id.nav_groupfolders) { - MainApp.showOnlyFilesOnDevice(false); + resetOnlyPersonalAndOnDevice(); Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setAction(FileDisplayActivity.LIST_GROUPFOLDERS); - intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); startActivity(intent); } else { if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && @@ -566,15 +640,14 @@ public abstract class DrawerActivity extends ToolbarActivity } } - private void startComposeActivity(ComposeDestination destination, int titleId, int menuItemId) { + private void startComposeActivity(ComposeDestination destination, int titleId) { Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class); composeActivity.putExtra(ComposeActivity.DESTINATION, destination); composeActivity.putExtra(ComposeActivity.TITLE, titleId); - composeActivity.putExtra(ComposeActivity.MENU_ITEM, menuItemId); startActivity(composeActivity); } - private void startActivity(Class activity) { + void startActivity(Class activity) { startActivity(new Intent(getApplicationContext(), activity)); } @@ -595,10 +668,7 @@ public abstract class DrawerActivity extends ToolbarActivity } public void openAddAccount() { - boolean isProviderOrOwnInstallationVisible = getResources() - .getBoolean(R.bool.show_provider_or_own_installation); - - if (isProviderOrOwnInstallationVisible) { + if (MDMConfig.INSTANCE.showIntro(this)) { Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class); firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true); startActivity(firstRunIntent); @@ -642,16 +712,11 @@ public abstract class DrawerActivity extends ToolbarActivity } private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) { + DrawerActivity.menuItemId = menuItemId; Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - - if (this instanceof ComposeActivity) { - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - } - intent.setAction(Intent.ACTION_SEARCH); intent.putExtra(OCFileListFragment.SEARCH_EVENT, searchEvent); - intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId); startActivity(intent); } @@ -679,7 +744,6 @@ public abstract class DrawerActivity extends ToolbarActivity externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.getName()); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, link.getUrl()); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, menuItem.getItemId()); startActivity(externalWebViewIntent); } } @@ -805,26 +869,26 @@ public abstract class DrawerActivity extends ToolbarActivity } private void unsetAllDrawerMenuItems() { - if (mNavigationView != null) { - mNavigationView.getMenu(); - Menu menu = mNavigationView.getMenu(); + if (drawerNavigationView != null) { + drawerNavigationView.getMenu(); + Menu menu = drawerNavigationView.getMenu(); for (int i = 0; i < menu.size(); i++) { menu.getItem(i).setChecked(false); } } - mCheckedMenuItem = Menu.NONE; + menuItemId = Menu.NONE; } private void updateQuotaLink() { if (mQuotaTextLink != null) { - if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) { + if (MDMConfig.INSTANCE.externalSiteSupport(this)) { List quotas = externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA); float density = getResources().getDisplayMetrics().density; final int size = Math.round(24 * density); - if (quotas.size() > 0) { + if (!quotas.isEmpty()) { final ExternalLink firstQuota = quotas.get(0); mQuotaTextLink.setText(firstQuota.getName()); mQuotaTextLink.setClickable(true); @@ -834,37 +898,19 @@ public abstract class DrawerActivity extends ToolbarActivity externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.getName()); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.getUrl()); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); + menuItemId = Menu.NONE; startActivity(externalWebViewIntent); }); - - SimpleTarget target = new SimpleTarget() { - @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { - Drawable test = resource.getCurrent(); - test.setBounds(0, 0, size, size); - mQuotaTextLink.setCompoundDrawablesWithIntrinsicBounds(test, null, null, null); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - - Drawable test = errorDrawable.getCurrent(); - test.setBounds(0, 0, size, size); - - mQuotaTextLink.setCompoundDrawablesWithIntrinsicBounds(test, null, null, null); - } - }; - - DisplayUtils.downloadIcon(getUserAccountManager(), - clientFactory, - this, - firstQuota.getIconUrl(), - target, - R.drawable.ic_link); - + Target quotaTarget = createQuotaDrawableTarget(size, mQuotaTextLink); + getClientRepository().getNextcloudClient(nextcloudClient -> { + GlideHelper.INSTANCE.loadIntoTarget(this, + nextcloudClient, + firstQuota.getIconUrl(), + quotaTarget, + R.drawable.ic_link); + return Unit.INSTANCE; + }); } else { mQuotaTextLink.setVisibility(View.GONE); } @@ -874,19 +920,58 @@ public abstract class DrawerActivity extends ToolbarActivity } } + private Target createQuotaDrawableTarget(int size, TextView quotaTextLink) { + return new CustomTarget<>() { + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + Drawable drawable = resource.getCurrent(); + drawable.setBounds(0, 0, size, size); + quotaTextLink.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + + Drawable drawable = errorDrawable != null ? errorDrawable.getCurrent() : null; + if (drawable != null) { + drawable.setBounds(0, 0, size, size); + quotaTextLink.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); + } + } + }; + } + + /** - * checks/highlights the provided menu item if the drawer has been initialized and the menu item exists. - * - * @param menuItemId the menu item to be highlighted + * Sets the menu item as checked in both the drawer and bottom navigation views, if applicable. */ - protected void setDrawerMenuItemChecked(int menuItemId) { - if (mNavigationView != null && mNavigationView.getMenu().findItem(menuItemId) != null) { - viewThemeUtils.platform.colorNavigationView(mNavigationView); - mCheckedMenuItem = menuItemId; - mNavigationView.getMenu().findItem(menuItemId).setChecked(true); - } else { - Log_OC.w(TAG, "setDrawerMenuItemChecked has been called with invalid menu-item-ID"); + @SuppressFBWarnings("RV") + public void setNavigationViewItemChecked() { + if (drawerNavigationView != null) { + MenuItem menuItem = drawerNavigationView.getMenu().findItem(menuItemId); + + if (menuItem != null && !menuItem.isChecked()) { + viewThemeUtils.platform.colorNavigationView(drawerNavigationView); + menuItem.setChecked(true); + } } + + if (bottomNavigationView != null) { + MenuItem menuItem = bottomNavigationView.getMenu().findItem(menuItemId); + + // Don't highlight assistant bottom navigation item because Assistant screen doesn't have same bottom navigation bar + if (menuItem != null && !menuItem.isChecked() && menuItem.getItemId() != R.id.nav_assistant) { + menuItem.setChecked(true); + } + } + + Log_OC.d(TAG, "New menu item is: " + menuItemId); } /** @@ -956,60 +1041,77 @@ public abstract class DrawerActivity extends ToolbarActivity } private void updateExternalLinksInDrawer() { - if (mNavigationView != null && getBaseContext().getResources().getBoolean(R.bool.show_external_links)) { - mNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links); + if (drawerNavigationView == null || !MDMConfig.INSTANCE.externalSiteSupport(this)) { + return; + } - int greyColor = ContextCompat.getColor(this, R.color.drawer_menu_icon); + drawerNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links); - for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) { - int id = mNavigationView.getMenu().add(R.id.drawer_menu_external_links, - MENU_ITEM_EXTERNAL_LINK + link.getId(), MENU_ORDER_EXTERNAL_LINKS, link.getName()) - .setCheckable(true).getItemId(); + int greyColor = ContextCompat.getColor(this, R.color.drawer_menu_icon); - MenuSimpleTarget target = new MenuSimpleTarget(id) { - @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { - setExternalLinkIcon(getIdMenuItem(), resource, greyColor); - } + for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) { + int id = drawerNavigationView + .getMenu() + .add(R.id.drawer_menu_external_links, + MENU_ITEM_EXTERNAL_LINK + + link.getId(), MENU_ORDER_EXTERNAL_LINKS, + link.getName() + ) + .setCheckable(true) + .getItemId(); - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - setExternalLinkIcon(getIdMenuItem(), errorDrawable, greyColor); - } - }; - - DisplayUtils.downloadIcon(getUserAccountManager(), - clientFactory, - this, - link.getIconUrl(), - target, - R.drawable.ic_link); - } - - setDrawerMenuItemChecked(mCheckedMenuItem); + Target iconTarget = createMenuItemTarget(id, greyColor); + getClientRepository().getNextcloudClient(nextcloudClient -> { + GlideHelper.INSTANCE.loadIntoTarget( + this, + nextcloudClient, + link.getIconUrl(), + iconTarget, + R.drawable.ic_link); + return Unit.INSTANCE; + }); } } - private void setExternalLinkIcon(int id, Drawable drawable, int greyColor) { - MenuItem menuItem = mNavigationView.getMenu().findItem(id); - - if (menuItem != null) { - if (drawable != null) { - menuItem.setIcon(viewThemeUtils.platform.colorDrawable(drawable, greyColor)); - } else { - menuItem.setIcon(R.drawable.ic_link); + private Target createMenuItemTarget(int menuItemId, int tintColor) { + return new CustomTarget<>() { + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + setExternalLinkIcon(menuItemId, resource, tintColor); } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + setExternalLinkIcon(menuItemId, errorDrawable, tintColor); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + }; + } + + private void setExternalLinkIcon(int id, Drawable drawable, int greyColor) { + MenuItem menuItem = drawerNavigationView.getMenu().findItem(id); + if (menuItem == null) { + return; } + + if (drawable == null) { + menuItem.setIcon(R.drawable.ic_link); + return; + } + + final var resizedDrawable = DrawableUtil.INSTANCE.getResizedDrawable(this, drawable,32); + menuItem.setIcon(viewThemeUtils.platform.colorDrawable(resizedDrawable, greyColor)); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (savedInstanceState != null) { mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false); - mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE); } externalLinksProvider = new ExternalLinksProvider(getContentResolver()); @@ -1019,22 +1121,14 @@ public abstract class DrawerActivity extends ToolbarActivity @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); - outState.putBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, mIsAccountChooserActive); - outState.putInt(KEY_CHECKED_MENU_ITEM, mCheckedMenuItem); } @Override public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false); - mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE); - - // check/highlight the menu item if present - if (mCheckedMenuItem > Menu.NONE || mCheckedMenuItem < Menu.NONE) { - setDrawerMenuItemChecked(mCheckedMenuItem); - } + setNavigationViewItemChecked(); } @Override @@ -1051,10 +1145,6 @@ public abstract class DrawerActivity extends ToolbarActivity updateQuotaLink(); } - public int getCheckedMenuItem() { - return mCheckedMenuItem; - } - @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -1078,12 +1168,6 @@ public abstract class DrawerActivity extends ToolbarActivity } } - @Override - protected void onResume() { - super.onResume(); - setDrawerMenuItemChecked(mCheckedMenuItem); - } - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -1139,6 +1223,11 @@ public abstract class DrawerActivity extends ToolbarActivity fetchExternalLinks(false); } + private void resetOnlyPersonalAndOnDevice() { + MainApp.showOnlyFilesOnDevice(false); + MainApp.showOnlyPersonalFiles(false); + } + /** * show the file list to the user. * @@ -1147,15 +1236,11 @@ public abstract class DrawerActivity extends ToolbarActivity public void showFiles(boolean onDeviceOnly, boolean onlyPersonalFiles) { MainApp.showOnlyFilesOnDevice(onDeviceOnly); MainApp.showOnlyPersonalFiles(onlyPersonalFiles); - Intent fileDisplayActivity = new Intent(getApplicationContext(), FileDisplayActivity.class); - fileDisplayActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - if (this instanceof ComposeActivity) { - fileDisplayActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - } - - fileDisplayActivity.setAction(FileDisplayActivity.ALL_FILES); - startActivity(fileDisplayActivity); + Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setAction(FileDisplayActivity.ALL_FILES); + startActivity(intent); } @Override @@ -1227,7 +1312,7 @@ public abstract class DrawerActivity extends ToolbarActivity * Retrieves external links via api from 'external' app */ public void fetchExternalLinks(final boolean force) { - if (!getBaseContext().getResources().getBoolean(R.bool.show_external_links)) { + if (!MDMConfig.INSTANCE.externalSiteSupport(this)) { return; } @@ -1239,7 +1324,8 @@ public abstract class DrawerActivity extends ToolbarActivity Thread t = new Thread(() -> { // fetch capabilities as early as possible - if ((getCapabilities() == null || getCapabilities().getAccountName() != null && getCapabilities().getAccountName().isEmpty()) + final OCCapability capability = getCapabilities(); + if ((capability == null || capability.getAccountName() == null || !capability.getAccountName().isEmpty()) && getStorageManager() != null) { GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation(getStorageManager()); getCapabilities.execute(getBaseContext()); @@ -1284,4 +1370,53 @@ public abstract class DrawerActivity extends ToolbarActivity }); t.start(); } + + protected void handleDeepLink(@NonNull Uri uri) { + String path = uri.getLastPathSegment(); + if (path == null) return; + + DeepLinkConstants deepLinkType = DeepLinkConstants.Companion.fromPath(path); + if (deepLinkType == null) { + DisplayUtils.showSnackMessage(this, getString(R.string.invalid_url)); + return; + } + + switch (deepLinkType) { + case OPEN_AUTO_UPLOAD: + startActivity(new Intent(this, SyncedFoldersActivity.class)); + break; + case OPEN_EXTERNAL_URL: + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri.getQueryParameter("url"))); + startActivity(intent); + break; + case ACTION_CREATE_NEW: + findViewById(R.id.fab_main).callOnClick(); + break; + case ACTION_APP_UPDATE: + LinkHelper.INSTANCE.openAppStore(getPackageName(), false, this); + break; + case OPEN_NOTIFICATIONS: + startActivity(NotificationsActivity.class); + break; + default: + handleNavItemClickEvent(deepLinkType.getNavId()); + break; + } + } + + private void handleNavItemClickEvent(@IdRes int menuItemId) { + if (drawerNavigationView == null) { + drawerNavigationView = findViewById(R.id.nav_view); + } + Menu navMenu = drawerNavigationView.getMenu(); + onNavigationItemClicked(navMenu.findItem(menuItemId)); + } + + public void showBottomNavigationBar(boolean show) { + ViewExtensionsKt.setVisibleIf(bottomNavigationView, show); + } + + public BottomNavigationView getBottomNavigationView() { + return bottomNavigationView; + } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/EditorWebView.java b/app/src/main/java/com/owncloud/android/ui/activity/EditorWebView.java index de413a8..132957c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/EditorWebView.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/EditorWebView.java @@ -3,18 +3,20 @@ * * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity; import android.app.DownloadManager; import android.content.ActivityNotFoundException; +import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.net.Uri; +import android.os.Environment; import android.os.Handler; import android.view.View; import android.webkit.JavascriptInterface; @@ -31,10 +33,12 @@ import com.owncloud.android.databinding.RichdocumentsWebviewBinding; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.ui.asynctasks.TextEditorLoadUrlTask; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.WebViewUtil; +import java.util.ArrayList; import java.util.Optional; import javax.inject.Inject; @@ -69,12 +73,15 @@ public abstract class EditorWebView extends ExternalSiteWebView { this.url = loadedUrl; if (!url.isEmpty()) { - new WebViewUtil(getApplicationContext()).setProxyKKPlus(this.getWebView()); + new WebViewUtil().setProxyKKPlus(this.getWebView()); try { Thread.sleep(1000); - } catch (InterruptedException e) { + } catch (InterruptedException ignored) { + } + + if (!url.equals(this.getWebView().getUrl())) { + this.getWebView().loadUrl(url); } - this.getWebView().loadUrl(url); new Handler().postDelayed(() -> { if (this.getWebView().getVisibility() != View.VISIBLE) { @@ -99,6 +106,23 @@ public abstract class EditorWebView extends ExternalSiteWebView { finish(); } + public void reload() { + if (getWebView().getVisibility() != View.VISIBLE) { + return; + } + + Optional user = getUser(); + if (!user.isPresent()) { + return; + } + + OCFile file = getFile(); + if (file != null) { + TextEditorLoadUrlTask task = new TextEditorLoadUrlTask(this, user.get(), file, editorUtils); + task.execute(); + } + } + @Override protected void bindView() { binding = RichdocumentsWebviewBinding.inflate(getLayoutInflater()); @@ -182,7 +206,20 @@ public abstract class EditorWebView extends ExternalSiteWebView { return; } - uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data)); + if (data.getClipData() == null) { + // one file + uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data)); + } else { + ArrayList uris = new ArrayList<>(); + // multiple files + for (int i = 0; i < data.getClipData().getItemCount(); i++) { + ClipData.Item item = data.getClipData().getItemAt(i); + uris.add(item.getUri()); + } + + uploadMessage.onReceiveValue(uris.toArray(new Uri[0])); + } + uploadMessage = null; } @@ -217,7 +254,7 @@ public abstract class EditorWebView extends ExternalSiteWebView { boolean isAutoUploadFolder = SyncedFolderProvider.isAutoUploadFolder(syncedFolderProvider, file, user); Integer overlayIconId = file.getFileOverlayIconId(isAutoUploadFolder); - LayerDrawable drawable = MimeTypeUtil.getFileIcon(preferences.isDarkModeEnabled(), overlayIconId, this, viewThemeUtils); + LayerDrawable drawable = MimeTypeUtil.getFolderIcon(preferences.isDarkModeEnabled(), overlayIconId, this, viewThemeUtils); binding.thumbnail.setImageDrawable(drawable); } else { if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) { @@ -247,7 +284,7 @@ public abstract class EditorWebView extends ExternalSiteWebView { } } - protected void downloadFile(Uri url) { + protected void downloadFile(Uri url, String fileName) { DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); if (downloadmanager == null) { @@ -259,6 +296,10 @@ public abstract class EditorWebView extends ExternalSiteWebView { request.allowScanningByMediaScanner(); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + // change the name file and your current activity. + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); + + downloadmanager.enqueue(request); } @@ -281,6 +322,11 @@ public abstract class EditorWebView extends ExternalSiteWebView { public void loaded() { runOnUiThread(EditorWebView.this::hideLoading); } + + @JavascriptInterface + public void reload() { + EditorWebView.this.reload(); + } } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java index c75c75e..8d94e81 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java @@ -7,7 +7,7 @@ * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2013 María Asensio Valverde * SPDX-FileCopyrightText: 2012-2013 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.ui.activity; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java b/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java index 1bb63ce..dad57ad 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity; @@ -44,14 +44,12 @@ public class ExternalSiteWebView extends FileActivity { public static final String EXTRA_URL = "URL"; public static final String EXTRA_SHOW_SIDEBAR = "SHOW_SIDEBAR"; public static final String EXTRA_SHOW_TOOLBAR = "SHOW_TOOLBAR"; - public static final String EXTRA_MENU_ITEM_ID = "MENU_ITEM_ID"; public static final String EXTRA_TEMPLATE = "TEMPLATE"; private static final String TAG = ExternalSiteWebView.class.getSimpleName(); protected boolean showToolbar = true; private ExternalsiteWebviewBinding binding; - private int menuItemId; private boolean showSidebar; String url; @@ -67,7 +65,6 @@ public class ExternalSiteWebView extends FileActivity { showToolbar = extras.getBoolean(EXTRA_SHOW_TOOLBAR); } - menuItemId = extras.getInt(EXTRA_MENU_ITEM_ID); showSidebar = extras.getBoolean(EXTRA_SHOW_SIDEBAR); // show progress @@ -107,8 +104,7 @@ public class ExternalSiteWebView extends FileActivity { } } - // setup drawer - setupDrawer(menuItemId); + setupDrawer(); if (!showSidebar) { setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); @@ -151,7 +147,7 @@ public class ExternalSiteWebView extends FileActivity { } }); - new WebViewUtil(getApplicationContext()).setProxyKKPlus(getWebView()); + new WebViewUtil().setProxyKKPlus(getWebView()); getWebView().loadUrl(url); } @@ -235,12 +231,6 @@ public class ExternalSiteWebView extends FileActivity { } } - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - setDrawerMenuItemChecked(menuItemId); - } - protected WebView getWebView() { return binding.webView; } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index b6b4c0b..f041d08 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -10,7 +10,7 @@ * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2013 David A. Velasco * SPDX-FileCopyrightText: 2011 Bartek Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.ui.activity; @@ -21,8 +21,10 @@ import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.net.ConnectivityManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -36,10 +38,13 @@ import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.jobs.download.FileDownloadWorker; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.network.ConnectivityService; +import com.nextcloud.receiver.NetworkChangeListener; +import com.nextcloud.receiver.NetworkChangeReceiver; import com.nextcloud.utils.EditorUtils; -import com.nextcloud.utils.extensions.ActivityExtensionsKt; import com.nextcloud.utils.extensions.BundleExtensionsKt; +import com.nextcloud.utils.extensions.FileExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; @@ -62,6 +67,7 @@ import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.operations.CreateShareViaLinkOperation; import com.owncloud.android.operations.CreateShareWithShareeOperation; import com.owncloud.android.operations.GetSharesForFileOperation; +import com.owncloud.android.operations.SetFilesDownloadLimitOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareOperation; @@ -79,17 +85,26 @@ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; import com.owncloud.android.ui.dialog.LoadingDialog; import com.owncloud.android.ui.dialog.ShareLinkToDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; +import com.owncloud.android.ui.events.DialogEvent; +import com.owncloud.android.ui.events.DialogEventType; +import com.owncloud.android.ui.events.FavoriteEvent; import com.owncloud.android.ui.fragment.FileDetailFragment; import com.owncloud.android.ui.fragment.FileDetailSharingFragment; import com.owncloud.android.ui.fragment.OCFileListFragment; +import com.owncloud.android.ui.fragment.filesRepository.FilesRepository; +import com.owncloud.android.ui.fragment.filesRepository.RemoteFilesRepository; import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.ui.preview.PreviewImageActivity; +import com.owncloud.android.ui.preview.PreviewMediaActivity; import com.owncloud.android.utils.ClipboardUtil; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.theme.ViewThemeUtils; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -111,9 +126,10 @@ import static com.owncloud.android.ui.activity.FileDisplayActivity.TAG_PUBLIC_LI */ public abstract class FileActivity extends DrawerActivity implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener, - LoadingVersionNumberTask.VersionDevInterface, FileDetailSharingFragment.OnEditShareListener { + LoadingVersionNumberTask.VersionDevInterface, FileDetailSharingFragment.OnEditShareListener, NetworkChangeListener { public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE"; + public static final String EXTRA_FILE_REMOTE_PATH = "com.owncloud.android.ui.activity.FILE_REMOTE_PATH"; public static final String EXTRA_LIVE_PHOTO_FILE = "com.owncloud.android.ui.activity.LIVE.PHOTO.FILE"; public static final String EXTRA_USER = "com.owncloud.android.ui.activity.USER"; public static final String EXTRA_FROM_NOTIFICATION = "com.owncloud.android.ui.activity.FROM_NOTIFICATION"; @@ -156,12 +172,12 @@ public abstract class FileActivity extends DrawerActivity protected FileDownloadWorker.FileDownloadProgressListener fileDownloadProgressListener; protected FileUploadHelper fileUploadHelper = FileUploadHelper.Companion.instance(); + protected boolean isFileDisplayActivityResumed = false; @Inject UserAccountManager accountManager; - @Inject - ConnectivityService connectivityService; + @Inject public ConnectivityService connectivityService; @Inject BackgroundJobManager backgroundJobManager; @@ -175,6 +191,15 @@ public abstract class FileActivity extends DrawerActivity @Inject ArbitraryDataProvider arbitraryDataProvider; + private NetworkChangeReceiver networkChangeReceiver; + + private FilesRepository filesRepository; + + private void registerNetworkChangeReceiver() { + IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(networkChangeReceiver, filter); + } + @Override public void showFiles(boolean onDeviceOnly, boolean personalFiles) { // must be specialized in subclasses @@ -197,10 +222,11 @@ public abstract class FileActivity extends DrawerActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + networkChangeReceiver = new NetworkChangeReceiver(this, connectivityService); usersAndGroupsSearchConfig.reset(); mHandler = new Handler(); mFileOperationsHelper = new FileOperationsHelper(this, getUserAccountManager(), connectivityService, editorUtils); - User user = null; + User user; if (savedInstanceState != null) { mFile = BundleExtensionsKt.getParcelableArgument(savedInstanceState, FileActivity.EXTRA_FILE, OCFile.class); @@ -226,11 +252,26 @@ public abstract class FileActivity extends DrawerActivity mOperationsServiceConnection = new OperationsServiceConnection(); bindService(new Intent(this, OperationsService.class), mOperationsServiceConnection, Context.BIND_AUTO_CREATE); + registerNetworkChangeReceiver(); + + filesRepository = new RemoteFilesRepository(getClientRepository(), this); } - public void checkInternetConnection() { - if (connectivityService != null && connectivityService.isConnected()) { + @Override + public void networkAndServerConnectionListener(boolean isNetworkAndServerAvailable) { + if (isNetworkAndServerAvailable) { hideInfoBox(); + + // No need to refresh the file list again since file display activity doing it. + if (!isFileDisplayActivityResumed) { + refreshList(); + } + } else { + if (this instanceof PreviewMediaActivity) { + hideInfoBox(); + } else { + showInfoBox(R.string.offline_mode); + } } } @@ -265,19 +306,26 @@ public abstract class FileActivity extends DrawerActivity mOperationsServiceBinder = null; } + unregisterReceiver(networkChangeReceiver); + super.onDestroy(); } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); + FileExtensionsKt.logFileSize(mFile, TAG); outState.putParcelable(FileActivity.EXTRA_FILE, mFile); outState.putBoolean(FileActivity.EXTRA_FROM_NOTIFICATION, mFromNotification); outState.putLong(KEY_WAITING_FOR_OP_ID, mFileOperationsHelper.getOpIdWaitingFor()); - if(getSupportActionBar() != null && getSupportActionBar().getTitle() != null) { - // Null check in case the actionbar is used in ActionBar.NAVIGATION_MODE_LIST - // since it doesn't have a title then - outState.putString(KEY_ACTION_BAR_TITLE, getSupportActionBar().getTitle().toString()); + + final var actionBar = getSupportActionBar(); + + if(actionBar != null) { + final var actionBarTitle = actionBar.getTitle(); + if (actionBarTitle != null) { + outState.putString(KEY_ACTION_BAR_TITLE, actionBarTitle.toString()); + } } } @@ -346,7 +394,7 @@ public abstract class FileActivity extends DrawerActivity (result.isException() && result.getException() instanceof AuthenticatorException) )) { - requestCredentialsUpdate(this); + requestCredentialsUpdate(); if (result.getCode() == ResultCode.UNAUTHORIZED) { DisplayUtils.showSnackMessage( @@ -395,6 +443,8 @@ public abstract class FileActivity extends DrawerActivity onUpdateShareInformation(result, R.string.sharee_add_failed); } else if (operation instanceof UpdateShareViaLinkOperation || operation instanceof UpdateShareInfoOperation) { onUpdateShareInformation(result, R.string.updating_share_failed); + } else if (operation instanceof SetFilesDownloadLimitOperation) { + onUpdateShareInformation(result, R.string.set_download_limit_failed); } else if (operation instanceof UpdateSharePermissionsOperation) { onUpdateShareInformation(result, R.string.updating_share_failed); } else if (operation instanceof UnshareOperation) { @@ -410,34 +460,24 @@ public abstract class FileActivity extends DrawerActivity * * Equivalent to call requestCredentialsUpdate(context, null); * - * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter - * to make the method accessible to {@link android.content.BroadcastReceiver}s. */ - protected void requestCredentialsUpdate(Context context) { - requestCredentialsUpdate(context, null); + protected void requestCredentialsUpdate() { + requestCredentialsUpdate(null); } /** * Invalidates the credentials stored for the given OC account and requests new credentials to the user, * navigating to {@link AuthenticatorActivity} * - * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter - * to make the method accessible to {@link android.content.BroadcastReceiver}s. * @param account Stored OC account to request credentials update for. If null, current account will * be used. */ - protected void requestCredentialsUpdate(Context context, Account account) { + protected void requestCredentialsUpdate(Account account) { if (account == null) { account = getAccount(); } - boolean remoteWipeSupported = accountManager.getServerVersion(account).isRemoteWipeSupported(); - - if (remoteWipeSupported) { - new CheckRemoteWipeTask(backgroundJobManager, account, new WeakReference<>(this)).execute(); - } else { - performCredentialsUpdate(account, context); - } + new CheckRemoteWipeTask(backgroundJobManager, account, new WeakReference<>(this)).execute(); } public void performCredentialsUpdate(Account account, Context context) { @@ -516,37 +556,48 @@ public abstract class FileActivity extends DrawerActivity } } - /** * Show loading dialog */ public void showLoadingDialog(String message) { - dismissLoadingDialog(); + runOnUiThread(() -> { + FragmentManager fragmentManager = getSupportFragmentManager(); + Fragment existingDialog = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); - Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG); - if (frag == null) { - Log_OC.d(TAG, "show loading dialog"); - LoadingDialog loading = LoadingDialog.newInstance(message); - FragmentManager fm = getSupportFragmentManager(); - FragmentTransaction ft = fm.beginTransaction(); - ft.add(loading, DIALOG_WAIT_TAG); - ft.commitAllowingStateLoss(); - } + if (existingDialog instanceof LoadingDialog loadingDialog) { + Log_OC.d(TAG, "dismiss previous loading dialog"); + loadingDialog.dismiss(); + } + + // Show new dialog + if (!fragmentManager.isStateSaved()) { + Log_OC.d(TAG, "show loading dialog"); + LoadingDialog loadingDialogFragment = LoadingDialog.newInstance(message); + loadingDialogFragment.show(fragmentManager, DIALOG_WAIT_TAG); + } + }); } /** * Dismiss loading dialog */ public void dismissLoadingDialog() { - Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG); - if (frag != null) { - Log_OC.d(TAG, "dismiss loading dialog"); - LoadingDialog loadingDialogFragment = (LoadingDialog) frag; - boolean isDialogFragmentReady = ActivityExtensionsKt.isDialogFragmentReady(this, loadingDialogFragment); - if (isDialogFragmentReady) { - loadingDialogFragment.dismiss(); + runOnUiThread(() -> { + FragmentManager fragmentManager = getSupportFragmentManager(); + Fragment fragment = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); + + if (fragment instanceof LoadingDialog loadingDialogFragment) { + Log_OC.d(TAG, "dismiss loading dialog"); + + // Avoid dismissing after state is saved + if (!fragmentManager.isStateSaved()) { + loadingDialogFragment.dismiss(); + } else { + // Dismiss allowing state loss if needed + loadingDialogFragment.dismissAllowingStateLoss(); + } } - } + }); } private void doOnResumeAndBound() { @@ -657,7 +708,7 @@ public abstract class FileActivity extends DrawerActivity Integer latestVersion, boolean openDirectly, boolean inBackground) { - Integer currentVersion = -1; + int currentVersion = -1; try { currentVersion = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode; } catch (PackageManager.NameNotFoundException e) { @@ -674,9 +725,7 @@ public abstract class FileActivity extends DrawerActivity } else { Snackbar.make(activity.findViewById(android.R.id.content), R.string.dev_version_new_version_available, Snackbar.LENGTH_LONG) - .setAction(activity.getString(R.string.version_dev_download), v -> { - DisplayUtils.startLinkIntent(activity, devApkLink); - }).show(); + .setAction(activity.getString(R.string.version_dev_download), v -> DisplayUtils.startLinkIntent(activity, devApkLink)).show(); } } else { if (!inBackground) { @@ -689,12 +738,14 @@ public abstract class FileActivity extends DrawerActivity OCFile file, String link, final ViewThemeUtils viewThemeUtils) { - ClipboardUtil.copyToClipboard(activity, link, false); - Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, - Snackbar.LENGTH_LONG) - .setAction(R.string.share, v -> showShareLinkDialog(activity, file, link)); - viewThemeUtils.material.themeSnackbar(snackbar); - snackbar.show(); + if (MDMConfig.INSTANCE.shareViaLink(activity) && MDMConfig.INSTANCE.clipBoardSupport(activity)) { + ClipboardUtil.copyToClipboard(activity, link, false); + Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, + Snackbar.LENGTH_LONG) + .setAction(R.string.share, v -> showShareLinkDialog(activity, file, link)); + viewThemeUtils.material.themeSnackbar(snackbar); + snackbar.show(); + } } public static void showShareLinkDialog(FileActivity activity, ServerFileInterface file, String link) { @@ -789,11 +840,14 @@ public abstract class FileActivity extends DrawerActivity String link = ""; OCFile file = null; for (Object object : result.getData()) { - OCShare shareLink = (OCShare) object; - if (TAG_PUBLIC_LINK.equalsIgnoreCase(shareLink.getShareType().name())) { - link = shareLink.getShareLink(); - file = getStorageManager().getFileByPath(shareLink.getPath()); - break; + if (object instanceof OCShare shareLink) { + ShareType shareType = shareLink.getShareType(); + + if (shareType != null && TAG_PUBLIC_LINK.equalsIgnoreCase(shareType.name())) { + link = shareLink.getShareLink(); + file = getStorageManager().getFileByEncryptedRemotePath(shareLink.getPath()); + break; + } } } @@ -803,8 +857,12 @@ public abstract class FileActivity extends DrawerActivity sharingFragment.onUpdateShareInformation(result, file); } - if (fileListFragment instanceof OCFileListFragment && file != null) { - ((OCFileListFragment) fileListFragment).updateOCFile(file); + if (fileListFragment instanceof OCFileListFragment ocFileListFragment && file != null) { + if (ocFileListFragment.getAdapterFiles().contains(file)) { + ocFileListFragment.updateOCFile(file); + } else { + DisplayUtils.showSnackMessage(this, R.string.file_activity_shared_file_cannot_be_updated); + } } } else { // Detect Failure (403) --> maybe needs password @@ -918,14 +976,12 @@ public abstract class FileActivity extends DrawerActivity * @param share * @param screenTypePermission * @param isReshareShown - * @param isExpiryDateShown */ @Override - public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown, - boolean isExpiryDateShown) { + public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown) { FileDetailFragment fragment = getFileDetailFragment(); if (fragment != null) { - fragment.editExistingShare(share, screenTypePermission, isReshareShown, isExpiryDateShown); + fragment.editExistingShare(share, screenTypePermission, isReshareShown); } } @@ -947,4 +1003,23 @@ public abstract class FileActivity extends DrawerActivity } return null; } + + public FilesRepository getFilesRepository() { + return filesRepository; + } + + public void showSyncLoadingDialog(boolean isFolder) { + if (isFolder) { + return; + } + + showLoadingDialog(getApplicationContext().getString(R.string.wait_a_moment)); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void handleSyncDialogEvent(DialogEvent event) { + if (event.getType() == DialogEventType.SYNC) { + dismissLoadingDialog(); + } + } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java deleted file mode 100644 index 3fc80bd..0000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ /dev/null @@ -1,2504 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2023 TSI-mc - * SPDX-FileCopyrightText: 2023 Archontis E. Kostis - * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-FileCopyrightText: 2018-2022 Tobias Kaminsky - * SPDX-FileCopyrightText: 2018-2020 Andy Scherzinger - * SPDX-FileCopyrightText: 2016 ownCloud Inc. - * SPDX-FileCopyrightText: 2012-2013 David A. Velasco - * SPDX-FileCopyrightText: 2011 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later - */ -package com.owncloud.android.ui.activity; - -import android.accounts.Account; -import android.accounts.AuthenticatorException; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Dialog; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.content.res.Resources.NotFoundException; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Parcelable; -import android.text.TextUtils; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewTreeObserver; -import android.view.WindowManager; - -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.snackbar.Snackbar; -import com.nextcloud.appReview.InAppReviewHelper; -import com.nextcloud.client.account.User; -import com.nextcloud.client.appinfo.AppInfo; -import com.nextcloud.client.core.AsyncRunner; -import com.nextcloud.client.di.Injectable; -import com.nextcloud.client.editimage.EditImageActivity; -import com.nextcloud.client.files.DeepLinkHandler; -import com.nextcloud.client.jobs.download.FileDownloadHelper; -import com.nextcloud.client.jobs.download.FileDownloadWorker; -import com.nextcloud.client.jobs.upload.FileUploadHelper; -import com.nextcloud.client.jobs.upload.FileUploadWorker; -import com.nextcloud.client.media.PlayerServiceConnection; -import com.nextcloud.client.network.ClientFactory; -import com.nextcloud.client.network.ConnectivityService; -import com.nextcloud.client.preferences.AppPreferences; -import com.nextcloud.client.utils.IntentUtil; -import com.nextcloud.model.WorkerState; -import com.nextcloud.model.WorkerStateLiveData; -import com.nextcloud.utils.extensions.ActivityExtensionsKt; -import com.nextcloud.utils.extensions.BundleExtensionsKt; -import com.nextcloud.utils.extensions.IntentExtensionsKt; -import com.nextcloud.utils.view.FastScrollUtils; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.databinding.FilesBinding; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.VirtualFolderType; -import com.owncloud.android.files.services.NameCollisionPolicy; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.files.RestoreFileVersionRemoteOperation; -import com.owncloud.android.lib.resources.files.SearchRemoteOperation; -import com.owncloud.android.operations.CopyFileOperation; -import com.owncloud.android.operations.CreateFolderOperation; -import com.owncloud.android.operations.DownloadType; -import com.owncloud.android.operations.MoveFileOperation; -import com.owncloud.android.operations.RefreshFolderOperation; -import com.owncloud.android.operations.RemoveFileOperation; -import com.owncloud.android.operations.RenameFileOperation; -import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.operations.UploadFileOperation; -import com.owncloud.android.syncadapter.FileSyncAdapter; -import com.owncloud.android.ui.asynctasks.CheckAvailableSpaceTask; -import com.owncloud.android.ui.asynctasks.FetchRemoteFileTask; -import com.owncloud.android.ui.asynctasks.GetRemoteFileTask; -import com.owncloud.android.ui.dialog.SendShareDialog; -import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; -import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment; -import com.owncloud.android.ui.events.SearchEvent; -import com.owncloud.android.ui.events.SyncEventFinished; -import com.owncloud.android.ui.events.TokenPushEvent; -import com.owncloud.android.ui.fragment.FileDetailFragment; -import com.owncloud.android.ui.fragment.FileFragment; -import com.owncloud.android.ui.fragment.GalleryFragment; -import com.owncloud.android.ui.fragment.GroupfolderListFragment; -import com.owncloud.android.ui.fragment.OCFileListFragment; -import com.owncloud.android.ui.fragment.SearchType; -import com.owncloud.android.ui.fragment.SharedListFragment; -import com.owncloud.android.ui.fragment.TaskRetainerFragment; -import com.owncloud.android.ui.fragment.UnifiedSearchFragment; -import com.owncloud.android.ui.helpers.FileOperationsHelper; -import com.owncloud.android.ui.helpers.UriUploader; -import com.owncloud.android.ui.preview.PreviewImageActivity; -import com.owncloud.android.ui.preview.PreviewImageFragment; -import com.owncloud.android.ui.preview.PreviewMediaActivity; -import com.owncloud.android.ui.preview.PreviewMediaFragment; -import com.owncloud.android.ui.preview.PreviewTextFileFragment; -import com.owncloud.android.ui.preview.PreviewTextFragment; -import com.owncloud.android.ui.preview.PreviewTextStringFragment; -import com.owncloud.android.ui.preview.pdf.PreviewPdfFragment; -import com.owncloud.android.utils.DataHolderUtil; -import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.ErrorMessageAdapter; -import com.owncloud.android.utils.FileSortOrder; -import com.owncloud.android.utils.MimeTypeUtil; -import com.owncloud.android.utils.PermissionUtil; -import com.owncloud.android.utils.PushUtils; -import com.owncloud.android.utils.StringUtils; -import com.owncloud.android.utils.theme.CapabilityUtils; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -import javax.inject.Inject; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.SearchView; -import androidx.core.view.MenuItemCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import kotlin.Unit; - -import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; -import static com.owncloud.android.utils.PermissionUtil.PERMISSION_CHOICE_DIALOG_TAG; - -/** - * Displays, what files the user has available in his Nextcloud. This is the main view. - */ -public class FileDisplayActivity extends FileActivity - implements FileFragment.ContainerActivity, - OnEnforceableRefreshListener, SortingOrderDialogFragment.OnSortingOrderListener, - SendShareDialog.SendShareDialogDownloader, Injectable { - - public static final String RESTART = "RESTART"; - public static final String ALL_FILES = "ALL_FILES"; - public static final String LIST_GROUPFOLDERS = "LIST_GROUPFOLDERS"; - public static final int SINGLE_USER_SIZE = 1; - public static final String OPEN_FILE = "NC_OPEN_FILE"; - - private FilesBinding binding; - - private SyncBroadcastReceiver mSyncBroadcastReceiver; - private UploadFinishReceiver mUploadFinishReceiver; - private DownloadFinishReceiver mDownloadFinishReceiver; - private RemoteOperationResult mLastSslUntrustedServerResult; - @Inject LocalBroadcastManager localBroadcastManager; - - public static final String TAG_PUBLIC_LINK = "PUBLIC_LINK"; - public static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG"; - public static final String KEY_FILE_ID = "KEY_FILE_ID"; - public static final String KEY_FILE_PATH = "KEY_FILE_PATH"; - public static final String KEY_ACCOUNT = "KEY_ACCOUNT"; - public static final String KEY_IS_SORT_GROUP_VISIBLE = "KEY_IS_SORT_GROUP_VISIBLE"; - - private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW"; - private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS"; - private static final String KEY_WAITING_TO_SEND = "WAITING_TO_SEND"; - - public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS"; - - public static final String DRAWER_MENU_ID = "DRAWER_MENU_ID"; - - public static final int REQUEST_CODE__SELECT_CONTENT_FROM_APPS = REQUEST_CODE__LAST_SHARED + 1; - public static final int REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM = REQUEST_CODE__LAST_SHARED + 2; - public static final int REQUEST_CODE__MOVE_OR_COPY_FILES = REQUEST_CODE__LAST_SHARED + 3; - public static final int REQUEST_CODE__UPLOAD_FROM_CAMERA = REQUEST_CODE__LAST_SHARED + 5; - - protected static final long DELAY_TO_REQUEST_REFRESH_OPERATION_LATER = DELAY_TO_REQUEST_OPERATIONS_LATER + 350; - - private static final String TAG = FileDisplayActivity.class.getSimpleName(); - - public static final String TAG_LIST_OF_FILES = "LIST_OF_FILES"; - - public static final String TEXT_PREVIEW = "TEXT_PREVIEW"; - - private OCFile mWaitingToPreview; - - private boolean mSyncInProgress; - - private OCFile mWaitingToSend; - - private Collection mDrawerMenuItemstoShowHideList; - - public static final String KEY_IS_SEARCH_OPEN = "IS_SEARCH_OPEN"; - public static final String KEY_SEARCH_QUERY = "SEARCH_QUERY"; - - public static final String REFRESH_FOLDER_EVENT_RECEIVER = "REFRESH_FOLDER_EVENT"; - - private String searchQuery = ""; - private boolean searchOpen; - - private SearchView searchView; - private PlayerServiceConnection mPlayerConnection; - private Optional lastDisplayedUser = Optional.empty(); - private int menuItemId = -1; - - @Inject AppPreferences preferences; - - @Inject AppInfo appInfo; - - @Inject ConnectivityService connectivityService; - - @Inject InAppReviewHelper inAppReviewHelper; - - @Inject FastScrollUtils fastScrollUtils; - @Inject AsyncRunner asyncRunner; - - public static Intent openFileIntent(Context context, User user, OCFile file) { - final Intent intent = new Intent(context, PreviewImageActivity.class); - intent.putExtra(FileActivity.EXTRA_FILE, file); - intent.putExtra(FileActivity.EXTRA_USER, user); - return intent; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - Log_OC.v(TAG, "onCreate() start"); - // Set the default theme to replace the launch screen theme. - setTheme(R.style.Theme_ownCloud_Toolbar_Drawer); - - super.onCreate(savedInstanceState); - loadSavedInstanceState(savedInstanceState); - - /// USER INTERFACE - initLayout(); - initUI(); - initTaskRetainerFragment(); - - // Restoring after UI has been inflated. - if (savedInstanceState != null) { - showSortListGroup(savedInstanceState.getBoolean(KEY_IS_SORT_GROUP_VISIBLE)); - } - - if (Intent.ACTION_VIEW.equals(getIntent().getAction())) { - handleOpenFileViaIntent(getIntent()); - } - - mPlayerConnection = new PlayerServiceConnection(this); - - checkStoragePath(); - - initSyncBroadcastReceiver(); - observeWorkerState(); - registerRefreshFolderEventReceiver(); - } - - @SuppressWarnings("unchecked") - private void loadSavedInstanceState(Bundle savedInstanceState) { - if (savedInstanceState != null) { - mWaitingToPreview = BundleExtensionsKt.getParcelableArgument(savedInstanceState, KEY_WAITING_TO_PREVIEW, OCFile.class); - mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS); - mWaitingToSend = BundleExtensionsKt.getParcelableArgument(savedInstanceState, KEY_WAITING_TO_SEND, OCFile.class); - searchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY); - searchOpen = savedInstanceState.getBoolean(FileDisplayActivity.KEY_IS_SEARCH_OPEN, false); - } else { - mWaitingToPreview = null; - mSyncInProgress = false; - mWaitingToSend = null; - } - } - - private void initLayout() { - // Inflate and set the layout view - binding = FilesBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - } - - private void initUI() { - setupHomeSearchToolbarWithSortAndListButtons(); - mMenuButton.setOnClickListener(v -> openDrawer()); - mSwitchAccountButton.setOnClickListener(v -> showManageAccountsDialog()); - fastScrollUtils.fixAppBarForFastScroll(binding.appbar.appbar, binding.rootLayout); - } - - private void initTaskRetainerFragment() { - // Init Fragment without UI to retain AsyncTask across configuration changes - FragmentManager fm = getSupportFragmentManager(); - TaskRetainerFragment taskRetainerFragment = (TaskRetainerFragment) fm.findFragmentByTag(TaskRetainerFragment.FTAG_TASK_RETAINER_FRAGMENT); - if (taskRetainerFragment == null) { - taskRetainerFragment = new TaskRetainerFragment(); - fm.beginTransaction().add(taskRetainerFragment, TaskRetainerFragment.FTAG_TASK_RETAINER_FRAGMENT).commit(); - } // else, Fragment already created and retained across configuration change - } - - private void checkStoragePath() { - String newStorage = Environment.getExternalStorageDirectory().getAbsolutePath(); - String storagePath = preferences.getStoragePath(newStorage); - if (!preferences.isStoragePathValid() && !new File(storagePath).exists()) { - // falling back to default - preferences.setStoragePath(newStorage); - preferences.setStoragePathValid(); - MainApp.setStoragePath(newStorage); - - try { - AlertDialog alertDialog = new AlertDialog.Builder(this, R.style.Theme_ownCloud_Dialog).setTitle(R.string.wrong_storage_path).setMessage(R.string.wrong_storage_path_desc).setPositiveButton(R.string.dialog_close, (dialog, which) -> dialog.dismiss()).setIcon(R.drawable.ic_settings).create(); - - alertDialog.show(); - viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)); - } catch (WindowManager.BadTokenException e) { - Log_OC.e(TAG, "Error showing wrong storage info, so skipping it: " + e.getMessage()); - } - } - } - - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - StoragePermissionDialogFragment fragment = (StoragePermissionDialogFragment) getSupportFragmentManager().findFragmentByTag(PERMISSION_CHOICE_DIALOG_TAG); - if (fragment != null) { - Dialog dialog = fragment.getDialog(); - - if (dialog != null && dialog.isShowing()) { - dialog.dismiss(); - getSupportFragmentManager().beginTransaction().remove(fragment).commitNowAllowingStateLoss(); - PermissionUtil.requestExternalStoragePermission(this, viewThemeUtils); - } - } - } - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - - // handle notification permission on API level >= 33 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - // request notification permission first and then prompt for storage permissions - // storage permissions handled in onRequestPermissionsResult - PermissionUtil.requestNotificationPermission(this); - } else { - PermissionUtil.requestExternalStoragePermission(this, viewThemeUtils); - } - - if (IntentExtensionsKt.getParcelableArgument(getIntent(), OCFileListFragment.SEARCH_EVENT, SearchEvent.class) != null) { - switchToSearchFragment(savedInstanceState); - - int menuId = getIntent().getIntExtra(DRAWER_MENU_ID, -1); - if (menuId != -1) { - setupDrawer(menuId); - } - } else { - createMinFragments(savedInstanceState); - syncAndUpdateFolder(true); - } - - if (OPEN_FILE.equals(getIntent().getAction())) { - getSupportFragmentManager().executePendingTransactions(); - onOpenFileIntent(getIntent()); - } else if (RESTART.equals(getIntent().getAction())) { - // most likely switched to different account - DisplayUtils.showSnackMessage(this, String.format(getString(R.string.logged_in_as), accountManager.getUser().getAccountName())); - } - - upgradeNotificationForInstantUpload(); - checkOutdatedServer(); - } - - private Activity getActivity() { - return this; - } - - /** - * For Android 7+. Opens a pop up info for the new instant upload and disabled the old instant upload. - */ - private void upgradeNotificationForInstantUpload() { - // check for Android 6+ if legacy instant upload is activated --> disable + show info - if (preferences.instantPictureUploadEnabled() || preferences.instantVideoUploadEnabled()) { - preferences.removeLegacyPreferences(); - // show info pop-up - new AlertDialog.Builder(this, R.style.Theme_ownCloud_Dialog).setTitle(R.string.drawer_synced_folders).setMessage(R.string.synced_folders_new_info).setPositiveButton(R.string.drawer_open, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // show instant upload - Intent syncedFoldersIntent = new Intent(getApplicationContext(), SyncedFoldersActivity.class); - dialog.dismiss(); - startActivity(syncedFoldersIntent); - } - }).setNegativeButton(R.string.drawer_close, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }).setIcon(R.drawable.nav_synced_folders).show(); - } - } - - private void checkOutdatedServer() { - Optional user = getUser(); - // show outdated warning - if (user.isPresent() && CapabilityUtils.checkOutdatedWarning(getResources(), user.get().getServer().getVersion(), getCapabilities().getExtendedSupport().isTrue())) { - DisplayUtils.showServerOutdatedSnackbar(this, Snackbar.LENGTH_LONG); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - switch (requestCode) { - case PermissionUtil.PERMISSIONS_POST_NOTIFICATIONS: - // handle notification permission on API level >= 33 - // dialogue was dismissed -> prompt for storage permissions - PermissionUtil.requestExternalStoragePermission(this, viewThemeUtils); - break; - case PermissionUtil.PERMISSIONS_EXTERNAL_STORAGE: - // If request is cancelled, result arrays are empty. - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - // permission was granted - EventBus.getDefault().post(new TokenPushEvent()); - syncAndUpdateFolder(true); - // toggle on is save since this is the only scenario this code gets accessed - } - break; - case PermissionUtil.PERMISSIONS_CAMERA: - // If request is cancelled, result arrays are empty. - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - // permission was granted - getFileOperationsHelper().uploadFromCamera(this, FileDisplayActivity.REQUEST_CODE__UPLOAD_FROM_CAMERA); - } - break; - default: - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - } - } - - private void switchToSearchFragment(Bundle savedInstanceState) { - if (savedInstanceState == null) { - OCFileListFragment listOfFiles = new OCFileListFragment(); - Bundle args = new Bundle(); - - args.putParcelable(OCFileListFragment.SEARCH_EVENT, - IntentExtensionsKt.getParcelableArgument(getIntent(), - OCFileListFragment.SEARCH_EVENT, - SearchEvent.class)); - args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true); - - listOfFiles.setArguments(args); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES); - transaction.commit(); - } else { - getSupportFragmentManager().findFragmentByTag(TAG_LIST_OF_FILES); - } - } - - private void createMinFragments(Bundle savedInstanceState) { - if (savedInstanceState == null) { - OCFileListFragment listOfFiles = new OCFileListFragment(); - Bundle args = new Bundle(); - args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true); - listOfFiles.setArguments(args); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES); - transaction.commit(); - } else { - getSupportFragmentManager().findFragmentByTag(TAG_LIST_OF_FILES); - } - } - - private void initFragments() { - /// First fragment - OCFileListFragment listOfFiles = getListOfFilesFragment(); - if (listOfFiles != null && TextUtils.isEmpty(searchQuery)) { - listOfFiles.listDirectory(getCurrentDir(), getFile(), MainApp.isOnlyOnDevice(), false); - } else { - Log_OC.e(TAG, "Still have a chance to lose the initialization of list fragment >("); - } - - /// reset views - resetTitleBarAndScrolling(); - } - - // Is called with the flag FLAG_ACTIVITY_SINGLE_TOP and set the new file and intent - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - setIntent(intent); - - if (ACTION_DETAILS.equalsIgnoreCase(intent.getAction())) { - OCFile file = IntentExtensionsKt.getParcelableArgument(intent, EXTRA_FILE, OCFile.class); - setFile(file); - setIntent(intent); - showDetails(file); - } else if (Intent.ACTION_VIEW.equals(intent.getAction())) { - handleOpenFileViaIntent(intent); - } else if (OPEN_FILE.equals(intent.getAction())) { - onOpenFileIntent(intent); - } else if (RESTART.equals(intent.getAction())) { - finish(); - startActivity(intent); - } else // Verify the action and get the query - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - setIntent(intent); - - SearchEvent searchEvent = IntentExtensionsKt.getParcelableArgument(intent, OCFileListFragment.SEARCH_EVENT, SearchEvent.class); - if (searchEvent != null) { - if (SearchRemoteOperation.SearchType.PHOTO_SEARCH == searchEvent.getSearchType()) { - Log_OC.d(this, "Switch to photo search fragment"); - - GalleryFragment photoFragment = new GalleryFragment(); - Bundle bundle = new Bundle(); - bundle.putParcelable(OCFileListFragment.SEARCH_EVENT, searchEvent); - photoFragment.setArguments(bundle); - setLeftFragment(photoFragment); - } else if (searchEvent.getSearchType() == SearchRemoteOperation.SearchType.SHARED_FILTER) { - Log_OC.d(this, "Switch to shared fragment"); - SharedListFragment sharedListFragment = new SharedListFragment(); - Bundle bundle = new Bundle(); - bundle.putParcelable(OCFileListFragment.SEARCH_EVENT, searchEvent); - sharedListFragment.setArguments(bundle); - setLeftFragment(sharedListFragment); - } else { - Log_OC.d(this, "Switch to oc file search fragment"); - - OCFileListFragment photoFragment = new OCFileListFragment(); - Bundle bundle = new Bundle(); - bundle.putParcelable(OCFileListFragment.SEARCH_EVENT, searchEvent); - photoFragment.setArguments(bundle); - setLeftFragment(photoFragment); - } - } - } else if (ALL_FILES.equals(intent.getAction())) { - Log_OC.d(this, "Switch to oc file fragment"); - - setLeftFragment(new OCFileListFragment()); - getSupportFragmentManager().executePendingTransactions(); - browseToRoot(); - } else if (LIST_GROUPFOLDERS.equals(intent.getAction())) { - Log_OC.d(this, "Switch to list groupfolders fragment"); - - setLeftFragment(new GroupfolderListFragment()); - getSupportFragmentManager().executePendingTransactions(); - } - } - - private void onOpenFileIntent(Intent intent) { - String extra = intent.getStringExtra(EXTRA_FILE); - OCFile file = getStorageManager().getFileByDecryptedRemotePath(extra); - if (file != null) { - OCFileListFragment fileFragment; - final Fragment leftFragment = getLeftFragment(); - if (leftFragment instanceof OCFileListFragment) { - fileFragment = (OCFileListFragment) leftFragment; - } else { - fileFragment = new OCFileListFragment(); - setLeftFragment(fileFragment); - } - fileFragment.onItemClicked(file); - } - } - - /** - * Replaces the first fragment managed by the activity with the received as a parameter. - * - * @param fragment New Fragment to set. - */ - private void setLeftFragment(Fragment fragment) { - setLeftFragment(fragment, true); - } - - private void setLeftFragment(Fragment fragment, boolean showSortListGroup) { - if (searchView != null) { - searchView.post(() -> searchView.setQuery(searchQuery, true)); - } - setDrawerIndicatorEnabled(false); - - //clear the subtitle while navigating to any other screen from Media screen - clearToolbarSubtitle(); - - showSortListGroup(showSortListGroup); - - FragmentManager fragmentManager = getSupportFragmentManager(); - if (!isFinishing() && !fragmentManager.isDestroyed()) { - FragmentTransaction transaction = fragmentManager.beginTransaction(); - transaction.addToBackStack(null); - transaction.replace(R.id.left_fragment_container, fragment, TAG_LIST_OF_FILES); - transaction.commit(); - } - } - - private OCFileListFragment getOCFileListFragmentFromFile() { - final Fragment leftFragment = getLeftFragment(); - OCFileListFragment listOfFiles; - - if (leftFragment instanceof OCFileListFragment) { - listOfFiles = (OCFileListFragment) leftFragment; - } else { - listOfFiles = new OCFileListFragment(); - Bundle args = new Bundle(); - args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true); - listOfFiles.setArguments(args); - - FragmentManager fm = getSupportFragmentManager(); - boolean isExecutingTransactions = !fm.isStateSaved() && !fm.executePendingTransactions(); - - if (isExecutingTransactions) { - setLeftFragment(listOfFiles); - fm.executePendingTransactions(); - } else { - new Handler(Looper.getMainLooper()).post(() -> { - setLeftFragment(listOfFiles); - fm.executePendingTransactions(); - }); - } - } - - return listOfFiles; - } - - - public void showFileActions(OCFile file) { - dismissLoadingDialog(); - OCFileListFragment listOfFiles = getOCFileListFragmentFromFile(); - browseUp(listOfFiles); - listOfFiles.onOverflowIconClicked(file, null); - } - - public @androidx.annotation.Nullable Fragment getLeftFragment() { - return getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); - } - - public @androidx.annotation.Nullable - @Deprecated OCFileListFragment getListOfFilesFragment() { - Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); - if (listOfFiles instanceof OCFileListFragment) { - return (OCFileListFragment) listOfFiles; - } - Log_OC.e(TAG, "Access to unexisting list of files fragment!!"); - return null; - } - - protected void resetTitleBarAndScrolling() { - updateActionBarTitleAndHomeButton(null); - resetScrolling(true); - } - - public void updateListOfFilesFragment(boolean fromSearch) { - OCFileListFragment fileListFragment = getListOfFilesFragment(); - if (fileListFragment != null) { - fileListFragment.listDirectory(MainApp.isOnlyOnDevice(), fromSearch); - } - } - - public void resetSearchView() { - OCFileListFragment fileListFragment = getListOfFilesFragment(); - - if (fileListFragment != null) { - fileListFragment.setSearchFragment(false); - } - } - - protected void refreshDetailsFragmentIfVisible(String downloadEvent, String downloadedRemotePath, boolean success) { - Fragment leftFragment = getLeftFragment(); - if (leftFragment instanceof FileDetailFragment) { - boolean waitedPreview = mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath); - FileDetailFragment detailsFragment = (FileDetailFragment) leftFragment; - OCFile fileInFragment = detailsFragment.getFile(); - if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) { - // the user browsed to other file ; forget the automatic preview - mWaitingToPreview = null; - - } else if (downloadEvent.equals(FileDownloadWorker.Companion.getDownloadAddedMessage())) { - // grant that the details fragment updates the progress bar - detailsFragment.listenForTransferProgress(); - detailsFragment.updateFileDetails(true, false); - - } else if (downloadEvent.equals(FileDownloadWorker.Companion.getDownloadFinishMessage())) { - // update the details panel - boolean detailsFragmentChanged = false; - if (waitedPreview) { - if (success) { - // update the file from database, for the local storage path - mWaitingToPreview = getStorageManager().getFileById(mWaitingToPreview.getFileId()); - - if (PreviewMediaActivity.Companion.canBePreviewed(mWaitingToPreview)) { - startMediaPreview(mWaitingToPreview, 0, true, true, true, true); - detailsFragmentChanged = true; - } else if (MimeTypeUtil.isVCard(mWaitingToPreview.getMimeType())) { - startContactListFragment(mWaitingToPreview); - detailsFragmentChanged = true; - } else if (PreviewTextFileFragment.canBePreviewed(mWaitingToPreview)) { - startTextPreview(mWaitingToPreview, true); - detailsFragmentChanged = true; - } else if (MimeTypeUtil.isPDF(mWaitingToPreview)) { - startPdfPreview(mWaitingToPreview); - detailsFragmentChanged = true; - } else { - getFileOperationsHelper().openFile(mWaitingToPreview); - } - } - mWaitingToPreview = null; - } - if (!detailsFragmentChanged) { - detailsFragment.updateFileDetails(false, success); - } - } - } - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - boolean drawerOpen = isDrawerOpen(); - - for (MenuItem menuItem : mDrawerMenuItemstoShowHideList) { - menuItem.setVisible(!drawerOpen); - } - - return super.onPrepareOptionsMenu(menu); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.activity_file_display, menu); - - menu.findItem(R.id.action_select_all).setVisible(false); - MenuItem searchMenuItem = menu.findItem(R.id.action_search); - searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem); - searchMenuItem.setVisible(false); - mSearchText.setOnClickListener(v -> { - showSearchView(); - searchView.setIconified(false); - }); - - viewThemeUtils.androidx.themeToolbarSearchView(searchView); - - // populate list of menu items to show/hide when drawer is opened/closed - mDrawerMenuItemstoShowHideList = new ArrayList<>(1); - mDrawerMenuItemstoShowHideList.add(searchMenuItem); - - //focus the SearchView - if (!TextUtils.isEmpty(searchQuery)) { - searchView.post(() -> { - searchView.setIconified(false); - searchView.setQuery(searchQuery, true); - searchView.clearFocus(); - }); - } - - final View mSearchEditFrame = searchView.findViewById(androidx.appcompat.R.id.search_edit_frame); - - searchView.setOnCloseListener(() -> { - if (TextUtils.isEmpty(searchView.getQuery().toString())) { - searchView.onActionViewCollapsed(); - setDrawerIndicatorEnabled(isDrawerIndicatorAvailable()); // order matters - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - mDrawerToggle.syncState(); - - OCFileListFragment ocFileListFragment = getListOfFilesFragment(); - if (ocFileListFragment != null) { - ocFileListFragment.setSearchFragment(false); - ocFileListFragment.refreshDirectory(); - } - } else { - searchView.post(() -> searchView.setQuery("", true)); - } - return true; - }); - - ViewTreeObserver vto = mSearchEditFrame.getViewTreeObserver(); - vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - int oldVisibility = -1; - - @Override - public void onGlobalLayout() { - - int currentVisibility = mSearchEditFrame.getVisibility(); - - if (currentVisibility != oldVisibility) { - if (currentVisibility == View.VISIBLE) { - setDrawerIndicatorEnabled(false); - } - - oldVisibility = currentVisibility; - } - - } - }); - - return super.onCreateOptionsMenu(menu); - } - - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean retval = true; - - int itemId = item.getItemId(); - - if (itemId == android.R.id.home) { - if (!isDrawerOpen() && !isSearchOpen() && isRoot(getCurrentDir()) && getLeftFragment() instanceof OCFileListFragment) { - openDrawer(); - } else { - onBackPressed(); - } - } else if (itemId == R.id.action_select_all) { - OCFileListFragment fragment = getListOfFilesFragment(); - - if (fragment != null) { - fragment.selectAllFiles(true); - } - } else { - retval = super.onOptionsItemSelected(item); - } - - return retval; - } - - /** - * Called, when the user selected something for uploading - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE__SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) { - - requestUploadOfContentFromApps(data, resultCode); - - } else if (requestCode == REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE || resultCode == UploadFilesActivity.RESULT_OK_AND_DO_NOTHING || resultCode == UploadFilesActivity.RESULT_OK_AND_DELETE)) { - - requestUploadOfFilesFromFileSystem(data, resultCode); - - } else if (requestCode == REQUEST_CODE__UPLOAD_FROM_CAMERA && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_DELETE)) { - - new CheckAvailableSpaceTask(new CheckAvailableSpaceTask.CheckAvailableSpaceListener() { - @Override - public void onCheckAvailableSpaceStart() { - Log_OC.d(this, "onCheckAvailableSpaceStart"); - } - - @Override - public void onCheckAvailableSpaceFinish(boolean hasEnoughSpaceAvailable, String... filesToUpload) { - Log_OC.d(this, "onCheckAvailableSpaceFinish"); - - if (hasEnoughSpaceAvailable) { - File file = new File(filesToUpload[0]); - File renamedFile = new File(file.getParent() + PATH_SEPARATOR + FileOperationsHelper.getCapturedImageName()); - - if (!file.renameTo(renamedFile)) { - DisplayUtils.showSnackMessage(getActivity(), "Fail to upload taken image!"); - return; - } - - requestUploadOfFilesFromFileSystem(renamedFile.getParentFile().getAbsolutePath(), new String[]{renamedFile.getAbsolutePath()}, FileUploadWorker.LOCAL_BEHAVIOUR_DELETE); - } - } - }, new String[]{FileOperationsHelper.createImageFile(getActivity()).getAbsolutePath()}).execute(); - } else if (requestCode == REQUEST_CODE__MOVE_OR_COPY_FILES && resultCode == RESULT_OK) { - exitSelectionMode(); - } else if (requestCode == PermissionUtil.REQUEST_CODE_MANAGE_ALL_FILES) { - syncAndUpdateFolder(true); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - - private void exitSelectionMode() { - OCFileListFragment ocFileListFragment = getListOfFilesFragment(); - if (ocFileListFragment != null) { - ocFileListFragment.exitSelectionMode(); - } - } - - private void requestUploadOfFilesFromFileSystem(Intent data, int resultCode) { - String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES); - String basePath = data.getStringExtra(UploadFilesActivity.LOCAL_BASE_PATH); - requestUploadOfFilesFromFileSystem(basePath, filePaths, resultCode); - } - - private String[] getRemotePaths(String directory, String[] filePaths, String localBasePath) { - String[] remotePaths = new String[filePaths.length]; - for (int j = 0; j < remotePaths.length; j++) { - String relativePath = StringUtils.removePrefix(filePaths[j], localBasePath); - remotePaths[j] = directory + relativePath; - } - - return remotePaths; - } - - private void requestUploadOfFilesFromFileSystem(String localBasePath, String[] filePaths, int resultCode) { - if (localBasePath != null && filePaths != null) { - if (!localBasePath.endsWith("/")) { - localBasePath = localBasePath + "/"; - } - - String remotePathBase = getCurrentDir().getRemotePath(); - String[] decryptedRemotePaths = getRemotePaths(remotePathBase, filePaths, localBasePath); - - int behaviour = switch (resultCode) { - case UploadFilesActivity.RESULT_OK_AND_MOVE -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE; - case UploadFilesActivity.RESULT_OK_AND_DELETE -> FileUploadWorker.LOCAL_BEHAVIOUR_DELETE; - default -> FileUploadWorker.LOCAL_BEHAVIOUR_FORGET; - }; - - FileUploadHelper.Companion.instance().uploadNewFiles(getUser().orElseThrow(RuntimeException::new), - filePaths, - decryptedRemotePaths, - behaviour, - true, - UploadFileOperation.CREATED_BY_USER, - false, - false, - NameCollisionPolicy.ASK_USER); - - } else { - Log_OC.d(TAG, "User clicked on 'Update' with no selection"); - DisplayUtils.showSnackMessage(this, R.string.filedisplay_no_file_selected); - } - } - - private void requestUploadOfContentFromApps(Intent contentIntent, int resultCode) { - - ArrayList streamsToUpload = new ArrayList<>(); - - if (contentIntent.getClipData() != null && contentIntent.getClipData().getItemCount() > 0) { - - for (int i = 0; i < contentIntent.getClipData().getItemCount(); i++) { - streamsToUpload.add(contentIntent.getClipData().getItemAt(i).getUri()); - } - - } else { - streamsToUpload.add(contentIntent.getData()); - } - - int behaviour = (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE) ? FileUploadWorker.LOCAL_BEHAVIOUR_MOVE : FileUploadWorker.LOCAL_BEHAVIOUR_COPY; - - OCFile currentDir = getCurrentDir(); - String remotePath = (currentDir != null) ? currentDir.getRemotePath() : OCFile.ROOT_PATH; - - UriUploader uploader = new UriUploader(this, streamsToUpload, remotePath, getUser().orElseThrow(RuntimeException::new), behaviour, false, // Not show waiting dialog while file is being copied from private storage - null // Not needed copy temp task listener - ); - - uploader.uploadUris(); - - } - - private boolean isSearchOpen() { - if (searchView == null) { - return false; - } else { - View mSearchEditFrame = searchView.findViewById(androidx.appcompat.R.id.search_edit_frame); - return mSearchEditFrame != null && mSearchEditFrame.getVisibility() == View.VISIBLE; - } - } - - private Boolean isRootDirectory() { - OCFile currentDir = getCurrentDir(); - return (currentDir == null || currentDir.getParentId() == FileDataStorageManager.ROOT_PARENT_ID); - } - - /* - * BackPressed priority/hierarchy: - * 1. close search view if opened - * 2. close drawer if opened - * 3. if it is OCFileListFragment and it's in Root -> (finish Activity) or it's not Root -> (browse up) - * 4. otherwise pop up the fragment and sortGroup view visibility and call super.onBackPressed() - */ - @SuppressFBWarnings("ITC_INHERITANCE_TYPE_CHECKING") - @Override - public void onBackPressed() { - final boolean isDrawerOpen = isDrawerOpen(); - final boolean isSearchOpen = isSearchOpen(); - - final Fragment leftFragment = getLeftFragment(); - - if (isSearchOpen) { - resetSearchAction(); - } else if (isDrawerOpen) { - super.onBackPressed(); - } else if (leftFragment instanceof OCFileListFragment listOfFiles) { - - // all closed - OCFile currentDir = getCurrentDir(); - if (isRoot(currentDir)) { - finish(); - return; - } - browseUp(listOfFiles); - } else { - popBack(); - } - } - - private void browseUp(OCFileListFragment listOfFiles) { - listOfFiles.onBrowseUp(); - setFile(listOfFiles.getCurrentFile()); - listOfFiles.setFabVisible(true); - listOfFiles.registerFabListener(); - resetTitleBarAndScrolling(); - setDrawerAllFiles(); - } - - private void resetSearchAction() { - Fragment leftFragment = getLeftFragment(); - if (isSearchOpen() && searchView != null) { - searchView.setQuery("", true); - searchView.onActionViewCollapsed(); - searchView.clearFocus(); - - if (isRoot(getCurrentDir()) && leftFragment instanceof OCFileListFragment listOfFiles) { - - // Remove the list to the original state - ArrayList listOfHiddenFiles = listOfFiles.getAdapter().listOfHiddenFiles; - listOfFiles.performSearch("", listOfHiddenFiles, true); - - hideSearchView(getCurrentDir()); - setDrawerIndicatorEnabled(isDrawerIndicatorAvailable()); - } - if (leftFragment instanceof UnifiedSearchFragment) { - showSortListGroup(false); - super.onBackPressed(); - } - } - } - - /** - * Use this method when want to pop the fragment on back press. It resets Scrolling (See - * {@link #resetScrolling(boolean) with true} and pop the visibility for sortListGroup (See - * {@link #showSortListGroup(boolean) with false}. At last call to super.onBackPressed() - */ - private void popBack() { - binding.fabMain.setImageResource(R.drawable.ic_plus); - resetScrolling(true); - showSortListGroup(false); - super.onBackPressed(); - } - - @Override - protected void onSaveInstanceState(@NonNull Bundle outState) { - // responsibility of restore is preferred in onCreate() before than in - // onRestoreInstanceState when there are Fragments involved - Log_OC.v(TAG, "onSaveInstanceState() start"); - super.onSaveInstanceState(outState); - outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview); - outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress); - // outState.putBoolean(FileDisplayActivity.KEY_REFRESH_SHARES_IN_PROGRESS, - // mRefreshSharesInProgress); - outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_SEND, mWaitingToSend); - if (searchView != null) { - outState.putBoolean(KEY_IS_SEARCH_OPEN, !searchView.isIconified()); - } - outState.putString(KEY_SEARCH_QUERY, searchQuery); - outState.putBoolean(KEY_IS_SORT_GROUP_VISIBLE, sortListGroupVisibility()); - Log_OC.v(TAG, "onSaveInstanceState() end"); - } - - @Override - protected void onResume() { - Log_OC.v(TAG, "onResume() start"); - super.onResume(); - // Instead of onPostCreate, starting the loading in onResume for children fragments - Fragment leftFragment = getLeftFragment(); - - // Listen for sync messages - if (!(leftFragment instanceof OCFileListFragment) || !((OCFileListFragment) leftFragment).isSearchFragment()) { - initSyncBroadcastReceiver(); - } - - if (!(leftFragment instanceof OCFileListFragment)) { - if (leftFragment instanceof FileFragment) { - super.updateActionBarTitleAndHomeButton(((FileFragment) leftFragment).getFile()); - } - return; - } - - OCFileListFragment ocFileListFragment = (OCFileListFragment) leftFragment; - - ocFileListFragment.setLoading(mSyncInProgress); - syncAndUpdateFolder(false, true); - - OCFile startFile = null; - if (getIntent() != null) { - OCFile fileArgs = IntentExtensionsKt.getParcelableArgument(getIntent(), EXTRA_FILE, OCFile.class); - if (fileArgs != null) { - startFile = fileArgs; - setFile(startFile); - } - } - - // refresh list of files - if (searchView != null && !TextUtils.isEmpty(searchQuery)) { - searchView.setQuery(searchQuery, false); - } else if (!ocFileListFragment.isSearchFragment() && startFile == null) { - updateListOfFilesFragment(false); - ocFileListFragment.registerFabListener(); - } else { - ocFileListFragment.listDirectory(startFile, false, false); - updateActionBarTitleAndHomeButton(startFile); - } - - // Listen for upload messages - IntentFilter uploadIntentFilter = new IntentFilter(FileUploadWorker.Companion.getUploadFinishMessage()); - mUploadFinishReceiver = new UploadFinishReceiver(); - localBroadcastManager.registerReceiver(mUploadFinishReceiver, uploadIntentFilter); - - // Listen for download messages - IntentFilter downloadIntentFilter = new IntentFilter(FileDownloadWorker.Companion.getDownloadAddedMessage()); - downloadIntentFilter.addAction(FileDownloadWorker.Companion.getDownloadFinishMessage()); - mDownloadFinishReceiver = new DownloadFinishReceiver(); - localBroadcastManager.registerReceiver(mDownloadFinishReceiver, downloadIntentFilter); - - // setup drawer - menuItemId = getIntent().getIntExtra(FileDisplayActivity.DRAWER_MENU_ID, -1); - - if (menuItemId == -1) { - setDrawerAllFiles(); - } else { - if (menuItemId == R.id.nav_all_files || menuItemId == R.id.nav_personal_files) { - setupHomeSearchToolbarWithSortAndListButtons(); - } else { - setupToolbar(); - } - setDrawerMenuItemChecked(menuItemId); - } - - if (ocFileListFragment instanceof GalleryFragment) { - updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_gallery)); - } - //show in-app review dialog to user - inAppReviewHelper.showInAppReview(this); - - Log_OC.v(TAG, "onResume() end"); - } - - private void setDrawerAllFiles() { - if (MainApp.isOnlyPersonFiles()) { - setDrawerMenuItemChecked(R.id.nav_personal_files); - setupHomeSearchToolbarWithSortAndListButtons(); - } else if (MainApp.isOnlyOnDevice()) { - setDrawerMenuItemChecked(R.id.nav_on_device); - setupToolbar(); - } else { - int lastMenuItem = getCheckedMenuItem(); - if (lastMenuItem == Menu.NONE) { - lastMenuItem = R.id.nav_all_files; - } - - setDrawerMenuItemChecked(lastMenuItem); - setupHomeSearchToolbarWithSortAndListButtons(); - } - } - - public void initSyncBroadcastReceiver() { - if (mSyncBroadcastReceiver == null) { - IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START); - syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END); - syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED); - syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED); - syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED); - mSyncBroadcastReceiver = new SyncBroadcastReceiver(); - localBroadcastManager.registerReceiver(mSyncBroadcastReceiver, syncIntentFilter); - } - } - - @Override - protected void onPause() { - Log_OC.v(TAG, "onPause() start"); - if (mSyncBroadcastReceiver != null) { - localBroadcastManager.unregisterReceiver(mSyncBroadcastReceiver); - mSyncBroadcastReceiver = null; - } - if (mUploadFinishReceiver != null) { - localBroadcastManager.unregisterReceiver(mUploadFinishReceiver); - mUploadFinishReceiver = null; - } - if (mDownloadFinishReceiver != null) { - localBroadcastManager.unregisterReceiver(mDownloadFinishReceiver); - mDownloadFinishReceiver = null; - } - - super.onPause(); - Log_OC.v(TAG, "onPause() end"); - } - - @Override - public void onSortingOrderChosen(FileSortOrder selection) { - OCFileListFragment ocFileListFragment = getListOfFilesFragment(); - if (ocFileListFragment != null) { - ocFileListFragment.sortFiles(selection); - } - } - - @Override - public void downloadFile(OCFile file, String packageName, String activityName) { - startDownloadForSending(file, OCFileListFragment.DOWNLOAD_SEND, packageName, activityName); - } - - private class SyncBroadcastReceiver extends BroadcastReceiver { - - /** - * {@link BroadcastReceiver} to enable syncing feedback in UI - */ - @SuppressLint("VisibleForTests") - @Override - public void onReceive(Context context, Intent intent) { - try { - String event = intent.getAction(); - Log_OC.d(TAG, "Received broadcast " + event); - String accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME); - - String synchFolderRemotePath = intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH); - RemoteOperationResult synchResult = (RemoteOperationResult) DataHolderUtil.getInstance().retrieve(intent.getStringExtra(FileSyncAdapter.EXTRA_RESULT)); - boolean sameAccount = getAccount() != null && accountName.equals(getAccount().name) && getStorageManager() != null; - - if (sameAccount) { - - if (FileSyncAdapter.EVENT_FULL_SYNC_START.equals(event)) { - mSyncInProgress = true; - - } else { - OCFile currentFile = (getFile() == null) ? null : getStorageManager().getFileByPath(getFile().getRemotePath()); - OCFile currentDir = (getCurrentDir() == null) ? null : getStorageManager().getFileByPath(getCurrentDir().getRemotePath()); - - if (currentDir == null) { - // current folder was removed from the server - DisplayUtils.showSnackMessage(getActivity(), R.string.sync_current_folder_was_removed, synchFolderRemotePath); - - browseToRoot(); - - } else { - if (currentFile == null && !getFile().isFolder()) { - // currently selected file was removed in the server, and now we - // know it - resetTitleBarAndScrolling(); - currentFile = currentDir; - } - - if (currentDir.getRemotePath().equals(synchFolderRemotePath)) { - OCFileListFragment fileListFragment = getListOfFilesFragment(); - if (fileListFragment != null) { - fileListFragment.listDirectory(currentDir, MainApp.isOnlyOnDevice(), false); - } - } - setFile(currentFile); - } - - mSyncInProgress = !FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) && !RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event); - - if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.equals(event) && synchResult != null) { - - if (synchResult.isSuccess()) { - hideInfoBox(); - } else { - // TODO refactor and make common - if (checkForRemoteOperationError(synchResult)) { - requestCredentialsUpdate(context); - } else { - switch (synchResult.getCode()) { - case SSL_RECOVERABLE_PEER_UNVERIFIED: - showUntrustedCertDialog(synchResult); - break; - - case MAINTENANCE_MODE: - showInfoBox(R.string.maintenance_mode); - break; - - case NO_NETWORK_CONNECTION: - showInfoBox(R.string.offline_mode); - break; - - case HOST_NOT_AVAILABLE: - showInfoBox(R.string.host_not_available); - break; - - default: - // nothing to do - break; - } - } - } - } - DataHolderUtil.getInstance().delete(intent.getStringExtra(FileSyncAdapter.EXTRA_RESULT)); - - Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress); - - - OCFileListFragment ocFileListFragment = getListOfFilesFragment(); - if (ocFileListFragment != null) { - ocFileListFragment.setLoading(mSyncInProgress); - if (!mSyncInProgress && !ocFileListFragment.isLoading()) { - // update scrolling when load finishes - if (ocFileListFragment.isEmpty()) { - lockScrolling(); - } else { - resetScrolling(false); - } - } - } - setBackgroundText(); - } - } - - if (synchResult != null && synchResult.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) { - mLastSslUntrustedServerResult = synchResult; - } - } catch (RuntimeException e) { - // avoid app crashes after changing the serial id of RemoteOperationResult - // in owncloud library with broadcast notifications pending to process - - try { - DataHolderUtil.getInstance().delete(intent.getStringExtra(FileSyncAdapter.EXTRA_RESULT)); - } catch (RuntimeException re) { - // we did not send this intent, so ignoring - Log_OC.i(TAG, "Ignoring error deleting data"); - } - } - } - } - - private boolean checkForRemoteOperationError(RemoteOperationResult syncResult) { - return ResultCode.UNAUTHORIZED == syncResult.getCode() || (syncResult.isException() && syncResult.getException() instanceof AuthenticatorException); - } - - /** - * Show a text message on screen view for notifying user if content is loading or folder is empty - */ - private void setBackgroundText() { - final OCFileListFragment ocFileListFragment = getListOfFilesFragment(); - if (ocFileListFragment != null) { - if (mSyncInProgress || getFile().getFileLength() > 0 && getStorageManager().getFolderContent(getFile(), false).isEmpty()) { - ocFileListFragment.setEmptyListLoadingMessage(); - } else { - if (MainApp.isOnlyOnDevice()) { - ocFileListFragment.setMessageForEmptyList(R.string.file_list_empty_headline, R.string.file_list_empty_on_device, R.drawable.ic_list_empty_folder, true); - } else { - ocFileListFragment.setEmptyListMessage(SearchType.NO_SEARCH); - } - } - } else { - Log_OC.e(TAG, "OCFileListFragment is null"); - } - } - - /** - * Once the file upload has finished -> update view - */ - private class UploadFinishReceiver extends BroadcastReceiver { - /** - * Once the file upload has finished -> update view - *

    - * {@link BroadcastReceiver} to enable upload feedback in UI - */ - @Override - public void onReceive(Context context, Intent intent) { - String uploadedRemotePath = intent.getStringExtra(FileUploadWorker.EXTRA_REMOTE_PATH); - String accountName = intent.getStringExtra(FileUploadWorker.ACCOUNT_NAME); - Account account = getAccount(); - boolean sameAccount = accountName != null && account != null && accountName.equals(account.name); - OCFile currentDir = getCurrentDir(); - boolean isDescendant = currentDir != null && uploadedRemotePath != null && uploadedRemotePath.startsWith(currentDir.getRemotePath()); - - if (sameAccount && isDescendant) { - String linkedToRemotePath = intent.getStringExtra(FileUploadWorker.EXTRA_LINKED_TO_PATH); - if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { - updateListOfFilesFragment(false); - } - } - - boolean uploadWasFine = intent.getBooleanExtra(FileUploadWorker.EXTRA_UPLOAD_RESULT, false); - boolean renamedInUpload = getFile().getRemotePath().equals(intent.getStringExtra(FileUploadWorker.EXTRA_OLD_REMOTE_PATH)); - - boolean sameFile = getFile().getRemotePath().equals(uploadedRemotePath) || renamedInUpload; - Fragment details = getLeftFragment(); - - if (sameAccount && sameFile && details instanceof FileDetailFragment) { - if (uploadWasFine) { - setFile(getStorageManager().getFileByPath(uploadedRemotePath)); - } else { - //TODO remove upload progress bar after upload failed. - Log_OC.d(TAG, "Remove upload progress bar after upload failed"); - } - if (renamedInUpload) { - String newName = new File(uploadedRemotePath).getName(); - DisplayUtils.showSnackMessage(getActivity(), R.string.filedetails_renamed_in_upload_msg, newName); - } - if (uploadWasFine || getFile().fileExists()) { - ((FileDetailFragment) details).updateFileDetails(false, true); - } else { - onBackPressed(); - } - - // Force the preview if the file is an image or text file - if (uploadWasFine) { - OCFile ocFile = getFile(); - if (PreviewImageFragment.canBePreviewed(ocFile)) { - startImagePreview(getFile(), true); - } else if (PreviewTextFileFragment.canBePreviewed(ocFile)) { - startTextPreview(ocFile, true); - } - // TODO what about other kind of previews? - } - } - OCFileListFragment ocFileListFragment = getListOfFilesFragment(); - if (ocFileListFragment != null) { - ocFileListFragment.setLoading(false); - } - } - - // TODO refactor this receiver, and maybe DownloadFinishReceiver; this method is duplicated :S - private boolean isAscendant(String linkedToRemotePath) { - OCFile currentDir = getCurrentDir(); - return currentDir != null && currentDir.getRemotePath().startsWith(linkedToRemotePath); - } - } - - - /** - * Class waiting for broadcast events from the {@link FileDownloadWorker} service. - *

    - * Updates the UI when a download is started or finished, provided that it is relevant for the current folder. - */ - private class DownloadFinishReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - boolean sameAccount = isSameAccount(intent); - String downloadedRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_REMOTE_PATH); - String downloadBehaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR); - boolean isDescendant = isDescendant(downloadedRemotePath); - - if (sameAccount && isDescendant) { - String linkedToRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_LINKED_TO_PATH); - if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { - updateListOfFilesFragment(false); - } - refreshDetailsFragmentIfVisible(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloadWorker.EXTRA_DOWNLOAD_RESULT, false)); - } - - if (mWaitingToSend != null) { - // update file after downloading - mWaitingToSend = getStorageManager().getFileByRemoteId(mWaitingToSend.getRemoteId()); - if (mWaitingToSend != null && mWaitingToSend.isDown() && OCFileListFragment.DOWNLOAD_SEND.equals(downloadBehaviour)) { - String packageName = intent.getStringExtra(SendShareDialog.PACKAGE_NAME); - String activityName = intent.getStringExtra(SendShareDialog.ACTIVITY_NAME); - - sendDownloadedFile(packageName, activityName); - } - } - - if (mWaitingToPreview != null) { - mWaitingToPreview = getStorageManager().getFileByRemoteId(mWaitingToPreview.getRemoteId()); - if (mWaitingToPreview != null && mWaitingToPreview.isDown() && EditImageActivity.OPEN_IMAGE_EDITOR.equals(downloadBehaviour)) { - startImageEditor(mWaitingToPreview); - } - } - } - - private boolean isDescendant(String downloadedRemotePath) { - OCFile currentDir = getCurrentDir(); - return currentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(currentDir.getRemotePath()); - } - - private boolean isAscendant(String linkedToRemotePath) { - OCFile currentDir = getCurrentDir(); - return currentDir != null && currentDir.getRemotePath().startsWith(linkedToRemotePath); - } - - private boolean isSameAccount(Intent intent) { - String accountName = intent.getStringExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME); - return accountName != null && getAccount() != null && accountName.equals(getAccount().name); - } - } - - - public void browseToRoot() { - OCFileListFragment listOfFiles = getListOfFilesFragment(); - if (listOfFiles != null) { // should never be null, indeed - OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH); - listOfFiles.listDirectory(root, MainApp.isOnlyOnDevice(), false); - setFile(listOfFiles.getCurrentFile()); - startSyncFolderOperation(root, false); - } - binding.fabMain.setImageResource(R.drawable.ic_plus); - resetTitleBarAndScrolling(); - } - - - @Override - public void onBrowsedDownTo(OCFile directory) { - setFile(directory); - resetTitleBarAndScrolling(); - // Sync Folder - startSyncFolderOperation(directory, false); - } - - /** - * Shows the information of the {@link OCFile} received as a parameter. - * - * @param file {@link OCFile} whose details will be shown - */ - @Override - public void showDetails(OCFile file) { - showDetails(file, 0); - } - - /** - * Shows the information of the {@link OCFile} received as a parameter. - * - * @param file {@link OCFile} whose details will be shown - * @param activeTab the active tab in the details view - */ - public void showDetails(OCFile file, int activeTab) { - User currentUser = getUser().orElseThrow(RuntimeException::new); - - resetScrolling(true); - - Fragment detailFragment = FileDetailFragment.newInstance(file, currentUser, activeTab); - setLeftFragment(detailFragment, false); - configureToolbarForPreview(file); - } - - /** - * Prevents content scrolling and toolbar collapse - */ - @VisibleForTesting - public void lockScrolling() { - binding.appbar.appbar.setExpanded(true, false); - final AppBarLayout.LayoutParams appbarParams = (AppBarLayout.LayoutParams) binding.appbar.toolbarFrame.getLayoutParams(); - appbarParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL); - binding.appbar.toolbarFrame.setLayoutParams(appbarParams); - } - - /** - * Resets content scrolling and toolbar collapse - */ - @VisibleForTesting - public void resetScrolling(boolean expandAppBar) { - AppBarLayout.LayoutParams appbarParams = (AppBarLayout.LayoutParams) binding.appbar.toolbarFrame.getLayoutParams(); - appbarParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); - binding.appbar.toolbarFrame.setLayoutParams(appbarParams); - if (expandAppBar) { - binding.appbar.appbar.setExpanded(true, false); - } - } - - @Override - public void updateActionBarTitleAndHomeButton(OCFile chosenFile) { - if (chosenFile == null) { - chosenFile = getFile(); // if no file is passed, current file decides - } - super.updateActionBarTitleAndHomeButton(chosenFile); - } - - @Override - public boolean isDrawerIndicatorAvailable() { - return isRoot(getCurrentDir()); - } - - private void observeWorkerState() { - WorkerStateLiveData.Companion.instance().observe(this, state -> { - if (state instanceof WorkerState.Download) { - Log_OC.d(TAG, "Download worker started"); - handleDownloadWorkerState(); - } else if (state instanceof WorkerState.Idle) { - fileDownloadProgressListener = null; - } - }); - } - - private void handleDownloadWorkerState() { - if (mWaitingToPreview != null && getStorageManager() != null) { - mWaitingToPreview = getStorageManager().getFileById(mWaitingToPreview.getFileId()); - if (mWaitingToPreview != null && !mWaitingToPreview.isDown()) { - requestForDownload(); - } - } - } - - @Override - protected ServiceConnection newTransferenceServiceConnection() { - return new ListServiceConnection(); - } - - /** - * Defines callbacks for service binding, passed to bindService() - * TODO: Check if this can be removed since download and uploads uses work manager now. - */ - private class ListServiceConnection implements ServiceConnection { - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - } - - @Override - public void onServiceDisconnected(ComponentName component) { - if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloadWorker.class))) { - Log_OC.d(TAG, "Download service disconnected"); - fileDownloadProgressListener = null; - } - } - } - - /** - * Updates the view associated to the activity after the finish of some operation over files in the current - * account. - * - * @param operation Removal operation performed. - * @param result Result of the removal. - */ - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - super.onRemoteOperationFinish(operation, result); - - if (operation instanceof RemoveFileOperation) { - onRemoveFileOperationFinish((RemoveFileOperation) operation, result); - } else if (operation instanceof RenameFileOperation) { - onRenameFileOperationFinish((RenameFileOperation) operation, result); - } else if (operation instanceof SynchronizeFileOperation) { - onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result); - } else if (operation instanceof CreateFolderOperation) { - onCreateFolderOperationFinish((CreateFolderOperation) operation, result); - } else if (operation instanceof MoveFileOperation) { - onMoveFileOperationFinish((MoveFileOperation) operation, result); - } else if (operation instanceof CopyFileOperation) { - onCopyFileOperationFinish((CopyFileOperation) operation, result); - } else if (operation instanceof RestoreFileVersionRemoteOperation) { - onRestoreFileVersionOperationFinish(result); - } - } - - private void refreshShowDetails() { - Fragment details = getLeftFragment(); - if (details instanceof FileFragment) { - OCFile file = ((FileFragment) details).getFile(); - if (file != null) { - file = getStorageManager().getFileByPath(file.getRemotePath()); - if (details instanceof PreviewTextFragment) { - // Refresh OCFile of the fragment - ((PreviewTextFileFragment) details).updateFile(file); - } else { - showDetails(file); - } - } - supportInvalidateOptionsMenu(); - } - } - - /** - * Updates the view associated to the activity after the finish of an operation trying to remove a file. - * - * @param operation Removal operation performed. - * @param result Result of the removal. - */ - private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) { - - if (!operation.isInBackground()) { - DisplayUtils.showSnackMessage(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources())); - } - - if (result.isSuccess()) { - OCFile removedFile = operation.getFile(); - tryStopPlaying(removedFile); - Fragment leftFragment = getLeftFragment(); - - // check if file is still available, if so do nothing - boolean fileAvailable = getStorageManager().fileExists(removedFile.getFileId()); - if (leftFragment instanceof FileFragment && !fileAvailable && removedFile.equals(((FileFragment) leftFragment).getFile())) { - setFile(getStorageManager().getFileById(removedFile.getParentId())); - resetTitleBarAndScrolling(); - } - OCFile parentFile = getStorageManager().getFileById(removedFile.getParentId()); - if (parentFile != null && parentFile.equals(getCurrentDir())) { - updateListOfFilesFragment(false); - } else if (getLeftFragment() instanceof GalleryFragment galleryFragment) { - galleryFragment.onRefresh(); - } - supportInvalidateOptionsMenu(); - } else { - if (result.isSslRecoverableException()) { - mLastSslUntrustedServerResult = result; - showUntrustedCertDialog(mLastSslUntrustedServerResult); - } - } - } - - private void onRestoreFileVersionOperationFinish(RemoteOperationResult result) { - if (result.isSuccess()) { - OCFile file = getFile(); - - // delete old local copy - if (file.isDown()) { - List list = new ArrayList<>(); - list.add(file); - getFileOperationsHelper().removeFiles(list, true, true); - - // download new version, only if file was previously download - getFileOperationsHelper().syncFile(file); - } - - OCFile parent = getStorageManager().getFileById(file.getParentId()); - startSyncFolderOperation(parent, true, true); - - Fragment leftFragment = getLeftFragment(); - if (leftFragment instanceof FileDetailFragment) { - FileDetailFragment fileDetailFragment = (FileDetailFragment) leftFragment; - fileDetailFragment.getFileDetailActivitiesFragment().reload(); - } - - DisplayUtils.showSnackMessage(this, R.string.file_version_restored_successfully); - } else { - DisplayUtils.showSnackMessage(this, R.string.file_version_restored_error); - } - } - - private void tryStopPlaying(OCFile file) { - // placeholder for stop-on-delete future code - if (mPlayerConnection != null && MimeTypeUtil.isAudio(file) && mPlayerConnection.isPlaying()) { - mPlayerConnection.stop(file); - } - } - - /** - * Updates the view associated to the activity after the finish of an operation trying to move a file. - * - * @param operation Move operation performed. - * @param result Result of the move operation. - */ - private void onMoveFileOperationFinish(MoveFileOperation operation, RemoteOperationResult result) { - if (result.isSuccess()) { - syncAndUpdateFolder(true); - } else { - try { - DisplayUtils.showSnackMessage(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources())); - - } catch (NotFoundException e) { - Log_OC.e(TAG, "Error while trying to show fail message ", e); - } - } - } - - /** - * Updates the view associated to the activity after the finish of an operation trying to copy a file. - * - * @param operation Copy operation performed. - * @param result Result of the copy operation. - */ - private void onCopyFileOperationFinish(CopyFileOperation operation, RemoteOperationResult result) { - if (result.isSuccess()) { - updateListOfFilesFragment(false); - } else { - try { - DisplayUtils.showSnackMessage(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources())); - - } catch (NotFoundException e) { - Log_OC.e(TAG, "Error while trying to show fail message ", e); - } - } - } - - /** - * Updates the view associated to the activity after the finish of an operation trying to rename a file. - * - * @param operation Renaming operation performed. - * @param result Result of the renaming. - */ - private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) { - Optional optionalUser = getUser(); - OCFile renamedFile = operation.getFile(); - if (result.isSuccess() && optionalUser.isPresent()) { - final User currentUser = optionalUser.get(); - Fragment leftFragment = getLeftFragment(); - if (leftFragment instanceof FileFragment) { - final FileFragment fileFragment = (FileFragment) leftFragment; - if (fileFragment instanceof FileDetailFragment && renamedFile.equals(fileFragment.getFile())) { - ((FileDetailFragment) fileFragment).updateFileDetails(renamedFile, currentUser); - showDetails(renamedFile); - - } else if (fileFragment instanceof PreviewMediaFragment && renamedFile.equals(fileFragment.getFile())) { - ((PreviewMediaFragment) fileFragment).updateFile(renamedFile); - if (PreviewMediaFragment.canBePreviewed(renamedFile)) { - long position = ((PreviewMediaFragment) fileFragment).getPosition(); - startMediaPreview(renamedFile, position, true, true, true, false); - } else { - getFileOperationsHelper().openFile(renamedFile); - } - } else if (fileFragment instanceof PreviewTextFragment && renamedFile.equals(fileFragment.getFile())) { - ((PreviewTextFileFragment) fileFragment).updateFile(renamedFile); - if (PreviewTextFileFragment.canBePreviewed(renamedFile)) { - startTextPreview(renamedFile, true); - } else { - getFileOperationsHelper().openFile(renamedFile); - } - } - } - - OCFile file = getStorageManager().getFileById(renamedFile.getParentId()); - if (file != null && file.equals(getCurrentDir())) { - updateListOfFilesFragment(false); - } - - } else { - DisplayUtils.showSnackMessage(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources())); - - if (result.isSslRecoverableException()) { - mLastSslUntrustedServerResult = result; - showUntrustedCertDialog(mLastSslUntrustedServerResult); - } - } - } - - - private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) { - if (result.isSuccess() && operation.transferWasRequested()) { - OCFile syncedFile = operation.getLocalFile(); - onTransferStateChanged(syncedFile, true, true); - supportInvalidateOptionsMenu(); - refreshShowDetails(); - } - } - - /** - * Updates the view associated to the activity after the finish of an operation trying create a new folder - * - * @param operation Creation operation performed. - * @param result Result of the creation. - */ - private void onCreateFolderOperationFinish(CreateFolderOperation operation, RemoteOperationResult result) { - if (result.isSuccess()) { - OCFileListFragment fileListFragment = getListOfFilesFragment(); - if (fileListFragment != null) { - fileListFragment.onItemClicked(getStorageManager().getFileByDecryptedRemotePath(operation.getRemotePath())); - } - } else { - try { - if (ResultCode.FOLDER_ALREADY_EXISTS == result.getCode()) { - DisplayUtils.showSnackMessage(this, R.string.folder_already_exists); - } else { - DisplayUtils.showSnackMessage(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources())); - } - } catch (NotFoundException e) { - Log_OC.e(TAG, "Error while trying to show fail message ", e); - } - } - } - - - /** - * {@inheritDoc} - */ - @Override - public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) { - updateListOfFilesFragment(false); - Fragment leftFragment = getLeftFragment(); - Optional optionalUser = getUser(); - if (leftFragment instanceof FileDetailFragment && file.equals(((FileDetailFragment) leftFragment).getFile()) && optionalUser.isPresent()) { - final User currentUser = optionalUser.get(); - if (downloading || uploading) { - ((FileDetailFragment) leftFragment).updateFileDetails(file, currentUser); - } else { - if (!file.fileExists()) { - resetTitleBarAndScrolling(); - } else { - ((FileDetailFragment) leftFragment).updateFileDetails(false, true); - } - } - } - - } - - - private void requestForDownload() { - User user = getUser().orElseThrow(RuntimeException::new); - FileDownloadHelper.Companion.instance().downloadFileIfNotStartedBefore(user, mWaitingToPreview); - } - - @Override - public void onSavedCertificate() { - startSyncFolderOperation(getCurrentDir(), false); - } - - /** - * Starts an operation to refresh the requested folder. - *

    - * The operation is run in a new background thread created on the fly. - *

    - * The refresh updates is a "light sync": properties of regular files in folder are updated (including associated - * shares), but not their contents. Only the contents of files marked to be kept-in-sync are synchronized too. - * - * @param folder Folder to refresh. - * @param ignoreETag If 'true', the data from the server will be fetched and sync'ed even if the eTag didn't - * change. - */ - public void startSyncFolderOperation(OCFile folder, boolean ignoreETag) { - startSyncFolderOperation(folder, ignoreETag, false); - } - - /** - * Starts an operation to refresh the requested folder. - *

    - * The operation is run in a new background thread created on the fly. - *

    - * The refresh updates is a "light sync": properties of regular files in folder are updated (including associated - * shares), but not their contents. Only the contents of files marked to be kept-in-sync are synchronized too. - * - * @param folder Folder to refresh. - * @param ignoreETag If 'true', the data from the server will be fetched and sync'ed even if the eTag didn't - * change. - * @param ignoreFocus reloads file list even without focus, e.g. on tablet mode, focus can still be in detail view - */ - public void startSyncFolderOperation(final OCFile folder, final boolean ignoreETag, boolean ignoreFocus) { - - // the execution is slightly delayed to allow the activity get the window focus if it's being started - // or if the method is called from a dialog that is being dismissed - if (TextUtils.isEmpty(searchQuery) && getUser().isPresent()) { - getHandler().postDelayed(() -> { - Optional user = getUser(); - - if (!ignoreFocus && !hasWindowFocus() || !user.isPresent()) { - // do not refresh if the user rotates the device while another window has focus - // or if the current user is no longer valid - return; - } - - long currentSyncTime = System.currentTimeMillis(); - mSyncInProgress = true; - - // perform folder synchronization - RemoteOperation refreshFolderOperation = new RefreshFolderOperation(folder, currentSyncTime, false, ignoreETag, getStorageManager(), user.get(), getApplicationContext()); - refreshFolderOperation.execute(getAccount(), MainApp.getAppContext(), FileDisplayActivity.this, null, null); - - OCFileListFragment fragment = getListOfFilesFragment(); - - if (fragment != null && !(fragment instanceof GalleryFragment)) { - fragment.setLoading(true); - } - - setBackgroundText(); - }, DELAY_TO_REQUEST_REFRESH_OPERATION_LATER); - } - } - - private void requestForDownload(OCFile file, String downloadBehaviour, String packageName, String activityName) { - final User currentUser = getUser().orElseThrow(RuntimeException::new); - if (!FileDownloadHelper.Companion.instance().isDownloading(currentUser, file)) { - FileDownloadHelper.Companion.instance().downloadFile(currentUser, file, downloadBehaviour, DownloadType.DOWNLOAD, activityName, packageName, null); - } - } - - private void sendDownloadedFile(String packageName, String activityName) { - if (mWaitingToSend != null) { - - Intent sendIntent = IntentUtil.createSendIntent(this, mWaitingToSend); - sendIntent.setComponent(new ComponentName(packageName, activityName)); - - // Show dialog - String sendTitle = getString(R.string.activity_chooser_send_file_title); - startActivity(Intent.createChooser(sendIntent, sendTitle)); - } else { - Log_OC.e(TAG, "Trying to send a NULL OCFile"); - } - - mWaitingToSend = null; - } - - /** - * Requests the download of the received {@link OCFile} , updates the UI to monitor the download progress and - * prepares the activity to send the file when the download finishes. - * - * @param file {@link OCFile} to download and preview. - * @param packageName - * @param activityName - */ - public void startDownloadForSending(OCFile file, String downloadBehaviour, String packageName, String activityName) { - mWaitingToSend = file; - requestForDownload(mWaitingToSend, downloadBehaviour, packageName, activityName); - } - - public void startImagePreview(OCFile file, boolean showPreview) { - Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class); - showDetailsIntent.putExtra(EXTRA_FILE, file); - showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo); - showDetailsIntent.putExtra(EXTRA_USER, getUser().orElseThrow(RuntimeException::new)); - if (showPreview) { - startActivity(showDetailsIntent); - } else { - FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this, getUserAccountManager(), connectivityService, editorUtils); - fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent); - } - } - - public void startImagePreview(OCFile file, VirtualFolderType type, boolean showPreview) { - Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class); - showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_FILE, file); - showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo); - showDetailsIntent.putExtra(EXTRA_USER, getUser().orElseThrow(RuntimeException::new)); - showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type); - - if (showPreview) { - startActivity(showDetailsIntent); - } else { - FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this, - getUserAccountManager(), - connectivityService, - editorUtils); - fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent); - } - } - - /** - * Stars the preview of an already down media {@link OCFile}. - * - * @param file Media {@link OCFile} to preview. - * @param startPlaybackPosition Media position where the playback will be started, in milliseconds. - * @param autoplay When 'true', the playback will start without user interactions. - */ - public void startMediaPreview(OCFile file, long startPlaybackPosition, boolean autoplay, boolean showPreview, boolean streamMedia, boolean showInActivity) { - Optional user = getUser(); - if (!user.isPresent()) { - return; // not reachable under normal conditions - } - if (showPreview && file.isDown() && !file.isDownloading() || streamMedia) { - if (showInActivity) { - startMediaActivity(file, startPlaybackPosition, autoplay, user); - } else { - configureToolbarForPreview(file); - Fragment mediaFragment = PreviewMediaFragment.newInstance(file, user.get(), startPlaybackPosition, autoplay, false); - setLeftFragment(mediaFragment, false); - } - } else { - Intent previewIntent = new Intent(); - previewIntent.putExtra(EXTRA_FILE, file); - previewIntent.putExtra(PreviewMediaFragment.EXTRA_START_POSITION, startPlaybackPosition); - previewIntent.putExtra(PreviewMediaFragment.EXTRA_AUTOPLAY, autoplay); - FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this, getUserAccountManager(), connectivityService, editorUtils); - fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent); - } - } - - private void startMediaActivity(OCFile file, long startPlaybackPosition, boolean autoplay, Optional user) { - Intent previewMediaIntent = new Intent(this, PreviewMediaActivity.class); - previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_FILE, file); - previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_USER, user.get()); - previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_START_POSITION, startPlaybackPosition); - previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_AUTOPLAY, autoplay); - startActivity(previewMediaIntent); - } - - public void configureToolbarForPreview(OCFile file) { - lockScrolling(); - super.updateActionBarTitleAndHomeButton(file); - } - - /** - * Starts the preview of a text file {@link OCFile}. - * - * @param file Text {@link OCFile} to preview. - */ - public void startTextPreview(OCFile file, boolean showPreview) { - Optional optUser = getUser(); - if (!optUser.isPresent()) { - // remnants of old unsafe system; do not crash, silently stop - return; - } - User user = optUser.get(); - if (showPreview) { - PreviewTextFileFragment fragment = PreviewTextFileFragment.create(user, file, searchOpen, searchQuery); - setLeftFragment(fragment, false); - configureToolbarForPreview(file); - } else { - Intent previewIntent = new Intent(); - previewIntent.putExtra(EXTRA_FILE, file); - previewIntent.putExtra(TEXT_PREVIEW, true); - FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this, getUserAccountManager(), connectivityService, editorUtils); - fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent); - } - } - - /** - * Starts rich workspace preview for a folder. - * - * @param folder {@link OCFile} to preview its rich workspace. - */ - public void startRichWorkspacePreview(OCFile folder) { - Bundle args = new Bundle(); - args.putParcelable(EXTRA_FILE, folder); - configureToolbarForPreview(folder); - Fragment textPreviewFragment = Fragment.instantiate(getApplicationContext(), PreviewTextStringFragment.class.getName(), args); - setLeftFragment(textPreviewFragment, false); - } - - public void startContactListFragment(OCFile file) { - final User user = getUser().orElseThrow(RuntimeException::new); - ContactsPreferenceActivity.startActivityWithContactsFile(this, user, file); - } - - public void startPdfPreview(OCFile file) { - if (getFileOperationsHelper().canOpenFile(file)) { - // prefer third party PDF apps - getFileOperationsHelper().openFile(file); - } else { - final Fragment pdfFragment = PreviewPdfFragment.newInstance(file); - - setLeftFragment(pdfFragment, false); - configureToolbarForPreview(file); - setMainFabVisible(false); - } - } - - - /** - * Requests the download of the received {@link OCFile} , updates the UI to monitor the download progress and - * prepares the activity to preview or open the file when the download finishes. - * - * @param file {@link OCFile} to download and preview. - * @param parentFolder {@link OCFile} containing above file - */ - public void startDownloadForPreview(OCFile file, OCFile parentFolder) { - final User currentUser = getUser().orElseThrow(RuntimeException::new); - Fragment detailFragment = FileDetailFragment.newInstance(file, parentFolder, currentUser); - setLeftFragment(detailFragment, false); - configureToolbarForPreview(file); - mWaitingToPreview = file; - requestForDownload(); - setFile(file); - } - - - /** - * Opens EditImageActivity with given file loaded. If file is not available locally, it will be synced before - * opening the image editor. - * - * @param file {@link OCFile} (image) to be loaded into image editor - */ - public void startImageEditor(OCFile file) { - if (file.isDown()) { - Intent editImageIntent = new Intent(this, EditImageActivity.class); - editImageIntent.putExtra(EditImageActivity.EXTRA_FILE, file); - startActivity(editImageIntent); - } else { - mWaitingToPreview = file; - requestForDownload(file, EditImageActivity.OPEN_IMAGE_EDITOR, getPackageName(), this.getClass().getSimpleName()); - updateActionBarTitleAndHomeButton(file); - setFile(file); - } - } - - - /** - * Request stopping the upload/download operation in progress over the given {@link OCFile} file. - * - * @param file {@link OCFile} file which operation are wanted to be cancel - */ - public void cancelTransference(OCFile file) { - getFileOperationsHelper().cancelTransference(file); - if (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(file.getRemotePath())) { - mWaitingToPreview = null; - } - if (mWaitingToSend != null && mWaitingToSend.getRemotePath().equals(file.getRemotePath())) { - mWaitingToSend = null; - } - onTransferStateChanged(file, false, false); - } - - /** - * Request stopping all upload/download operations in progress over the given {@link OCFile} files. - * - * @param files collection of {@link OCFile} files which operations are wanted to be cancel - */ - public void cancelTransference(Collection files) { - for (OCFile file : files) { - cancelTransference(file); - } - } - - @Override - public void onRefresh(boolean ignoreETag) { - syncAndUpdateFolder(ignoreETag); - } - - @Override - public void onRefresh() { - syncAndUpdateFolder(true); - } - - private void syncAndUpdateFolder(boolean ignoreETag) { - syncAndUpdateFolder(ignoreETag, false); - } - - private void syncAndUpdateFolder(boolean ignoreETag, boolean ignoreFocus) { - OCFileListFragment listOfFiles = getListOfFilesFragment(); - if (listOfFiles != null && !listOfFiles.isSearchFragment()) { - OCFile folder = listOfFiles.getCurrentFile(); - if (folder != null) { - startSyncFolderOperation(folder, ignoreETag, ignoreFocus); - } - } - } - - @Override - public void showFiles(boolean onDeviceOnly, boolean personalFiles) { - super.showFiles(onDeviceOnly, personalFiles); - if (onDeviceOnly) { - updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_on_device)); - } - OCFileListFragment ocFileListFragment = getListOfFilesFragment(); - if (ocFileListFragment != null && !(ocFileListFragment instanceof GalleryFragment) && !(ocFileListFragment instanceof SharedListFragment)) { - ocFileListFragment.refreshDirectory(); - } else { - setLeftFragment(new OCFileListFragment()); - } - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(final SearchEvent event) { - if (SearchRemoteOperation.SearchType.PHOTO_SEARCH == event.getSearchType()) { - Log_OC.d(this, "Switch to photo search fragment"); - setLeftFragment(new GalleryFragment()); - } else if (event.getSearchType() == SearchRemoteOperation.SearchType.SHARED_FILTER) { - Log_OC.d(this, "Switch to Shared fragment"); - setLeftFragment(new SharedListFragment()); - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(SyncEventFinished event) { - Bundle bundle = event.getIntent().getExtras(); - if (event.getIntent().getBooleanExtra(TEXT_PREVIEW, false)) { - startTextPreview((OCFile) bundle.get(EXTRA_FILE), true); - } else if (bundle.containsKey(PreviewMediaFragment.EXTRA_START_POSITION)) { - startMediaPreview((OCFile) bundle.get(EXTRA_FILE), (long) bundle.get(PreviewMediaFragment.EXTRA_START_POSITION), (boolean) bundle.get(PreviewMediaFragment.EXTRA_AUTOPLAY), true, true, true); - } else if (bundle.containsKey(PreviewImageActivity.EXTRA_VIRTUAL_TYPE)) { - startImagePreview((OCFile) bundle.get(EXTRA_FILE), (VirtualFolderType) bundle.get(PreviewImageActivity.EXTRA_VIRTUAL_TYPE), true); - } else { - startImagePreview((OCFile) bundle.get(EXTRA_FILE), true); - } - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(TokenPushEvent event) { - if (!preferences.isKeysReInitEnabled()) { - PushUtils.reinitKeys(getUserAccountManager()); - } else { - PushUtils.pushRegistrationToServer(getUserAccountManager(), preferences.getPushToken()); - } - } - - @Override - public void onStart() { - super.onStart(); - final Optional optionalUser = getUser(); - final FileDataStorageManager storageManager = getStorageManager(); - if (optionalUser.isPresent() && storageManager != null) { - /// Check whether the 'main' OCFile handled by the Activity is contained in the - // current Account - OCFile file = getFile(); - // get parent from path - String parentPath = ""; - if (file != null) { - if (file.isDown() && file.getLastSyncDateForProperties() == 0) { - // upload in progress - right now, files are not inserted in the local - // cache until the upload is successful get parent from path - parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName())); - if (storageManager.getFileByPath(parentPath) == null) { - file = null; // not able to know the directory where the file is uploading - } - } else { - file = storageManager.getFileByPath(file.getRemotePath()); - // currentDir = null if not in the current Account - } - } - if (file == null) { - // fall back to root folder - file = storageManager.getFileByPath(OCFile.ROOT_PATH); // never returns null - } - setFile(file); - - User user = optionalUser.get(); - setupDrawer(); - - mSwitchAccountButton.setTag(user.getAccountName()); - DisplayUtils.setAvatar(user, this, getResources().getDimension(R.dimen.nav_drawer_menu_avatar_radius), getResources(), mSwitchAccountButton, this); - final boolean userChanged = !user.nameEquals(lastDisplayedUser.orElse(null)); - if (userChanged) { - Log_OC.d(TAG, "Initializing Fragments in onAccountChanged.."); - initFragments(); - if (file.isFolder() && TextUtils.isEmpty(searchQuery)) { - startSyncFolderOperation(file, false); - } - } else { - updateActionBarTitleAndHomeButton(file.isFolder() ? null : file); - } - } - lastDisplayedUser = optionalUser; - - EventBus.getDefault().post(new TokenPushEvent()); - checkForNewDevVersionNecessary(getApplicationContext()); - } - - private void registerRefreshFolderEventReceiver() { - IntentFilter filter = new IntentFilter(REFRESH_FOLDER_EVENT_RECEIVER); - LocalBroadcastManager.getInstance(this).registerReceiver(refreshFolderEventReceiver, filter); - } - - private final BroadcastReceiver refreshFolderEventReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - syncAndUpdateFolder(true); - } - }; - - @Override - protected void onDestroy() { - LocalBroadcastManager.getInstance(this).unregisterReceiver(refreshFolderEventReceiver); - super.onDestroy(); - } - - @Override - protected void onRestart() { - super.onRestart(); - - checkForNewDevVersionNecessary(getApplicationContext()); - } - - public void setSearchQuery(String query) { - searchQuery = query; - } - - private void handleOpenFileViaIntent(Intent intent) { - showLoadingDialog(getString(R.string.retrieving_file)); - - String userName = intent.getStringExtra(KEY_ACCOUNT); - String fileId = intent.getStringExtra(KEY_FILE_ID); - String filePath = intent.getStringExtra(KEY_FILE_PATH); - - if (userName == null && fileId == null && intent.getData() != null) { - openDeepLink(intent.getData()); - } else { - Optional optionalUser = userName == null ? getUser() : getUserAccountManager().getUser(userName); - if (optionalUser.isPresent()) { - if (!TextUtils.isEmpty(fileId)) { - openFile(optionalUser.get(), fileId); - } else if (!TextUtils.isEmpty(filePath)) { - openFileByPath(optionalUser.get(), filePath); - } else { - dismissLoadingDialog(); - accountClicked(optionalUser.get().hashCode()); - } - } else { - dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.associated_account_not_found)); - } - } - } - - private void openDeepLink(Uri uri) { - DeepLinkHandler linkHandler = new DeepLinkHandler(getUserAccountManager()); - DeepLinkHandler.Match match = linkHandler.parseDeepLink(uri); - if (match == null) { - dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.invalid_url)); - } else if (match.getUsers().isEmpty()) { - dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.associated_account_not_found)); - } else if (match.getUsers().size() == SINGLE_USER_SIZE) { - openFile(match.getUsers().get(0), match.getFileId()); - } else { - selectUserAndOpenFile(match.getUsers(), match.getFileId()); - } - } - - private void selectUserAndOpenFile(List users, String fileId) { - final CharSequence[] userNames = new CharSequence[users.size()]; - for (int i = 0; i < userNames.length; i++) { - userNames[i] = users.get(i).getAccountName(); - } - final AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.common_choose_account).setItems(userNames, (dialog, which) -> { - User user = users.get(which); - openFile(user, fileId); - showLoadingDialog(getString(R.string.retrieving_file)); - }); - final AlertDialog dialog = builder.create(); - dismissLoadingDialog(); - dialog.show(); - } - - private void openFile(User user, String fileId) { - setUser(user); - - if (fileId == null) { - onFileRequestError(null); - return; - } - - FileDataStorageManager storageManager = getStorageManager(); - - if (storageManager == null) { - storageManager = new FileDataStorageManager(user, getContentResolver()); - } - - FetchRemoteFileTask fetchRemoteFileTask = new FetchRemoteFileTask(user, fileId, storageManager, this); - fetchRemoteFileTask.execute(); - } - - private void openFileByPath(User user, String filepath) { - setUser(user); - - if (filepath == null) { - onFileRequestError(null); - return; - } - - FileDataStorageManager storageManager = getStorageManager(); - - if (storageManager == null) { - storageManager = new FileDataStorageManager(user, getContentResolver()); - } - - OwnCloudClient client; - try { - client = clientFactory.create(user); - } catch (ClientFactory.CreationException e) { - onFileRequestError(null); - return; - } - - GetRemoteFileTask getRemoteFileTask = new GetRemoteFileTask(this, filepath, client, storageManager, user); - asyncRunner.postQuickTask(getRemoteFileTask, this::onFileRequestResult, this::onFileRequestError); - } - - private Unit onFileRequestError(Throwable throwable) { - dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.error_retrieving_file)); - Log_OC.e(TAG, "Requesting file from remote failed!", throwable); - return null; - } - - - private Unit onFileRequestResult(GetRemoteFileTask.Result result) { - dismissLoadingDialog(); - - setFile(result.getFile()); - - OCFileListFragment fileFragment = new OCFileListFragment(); - setLeftFragment(fileFragment); - - getSupportFragmentManager().executePendingTransactions(); - - fileFragment.onItemClicked(result.getFile()); - - return null; - } - - public void performUnifiedSearch(String query, ArrayList listOfHiddenFiles) { - UnifiedSearchFragment unifiedSearchFragment = UnifiedSearchFragment.Companion.newInstance(query, listOfHiddenFiles); - setLeftFragment(unifiedSearchFragment, false); - } - - public void setMainFabVisible(final boolean visible) { - final int visibility = visible ? View.VISIBLE : View.GONE; - binding.fabMain.setVisibility(visibility); - } - - public void showFile(OCFile selectedFile, String message) { - dismissLoadingDialog(); - - OCFileListFragment listOfFiles = getOCFileListFragmentFromFile(); - - if (TextUtils.isEmpty(message)) { - OCFile temp = getFile(); - setFile(getCurrentDir()); - listOfFiles.listDirectory(getCurrentDir(), temp, MainApp.isOnlyOnDevice(), false); - updateActionBarTitleAndHomeButton(null); - } else { - DisplayUtils.showSnackMessage(listOfFiles.getView(), message); - } - - if (selectedFile != null) { - listOfFiles.onItemClicked(selectedFile); - } - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt new file mode 100644 index 0000000..74a2a51 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -0,0 +1,3056 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2023-2024 TSI-mc + * SPDX-FileCopyrightText: 2023 Archontis E. Kostis + * SPDX-FileCopyrightText: 2019 Chris Narkiewicz + * SPDX-FileCopyrightText: 2018-2022 Tobias Kaminsky + * SPDX-FileCopyrightText: 2018-2020 Andy Scherzinger + * SPDX-FileCopyrightText: 2016 ownCloud Inc. + * SPDX-FileCopyrightText: 2012-2013 David A. Velasco + * SPDX-FileCopyrightText: 2011 Bartosz Przybylski + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + */ +package com.owncloud.android.ui.activity + +import android.accounts.AuthenticatorException +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.content.IntentFilter +import android.content.ServiceConnection +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.content.res.Resources +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.os.Handler +import android.os.IBinder +import android.os.Looper +import android.os.Parcelable +import android.text.TextUtils +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewTreeObserver.OnGlobalLayoutListener +import android.view.WindowManager.BadTokenException +import androidx.annotation.VisibleForTesting +import androidx.appcompat.widget.SearchView +import androidx.core.view.MenuItemCompat +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.lifecycleScope +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.appReview.InAppReviewHelper +import com.nextcloud.client.account.User +import com.nextcloud.client.appinfo.AppInfo +import com.nextcloud.client.core.AsyncRunner +import com.nextcloud.client.core.Clock +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.editimage.EditImageActivity +import com.nextcloud.client.files.DeepLinkHandler +import com.nextcloud.client.jobs.download.FileDownloadHelper +import com.nextcloud.client.jobs.download.FileDownloadWorker +import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadAddedMessage +import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadFinishMessage +import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.client.jobs.upload.FileUploadWorker +import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.getUploadFinishMessage +import com.nextcloud.client.media.PlayerServiceConnection +import com.nextcloud.client.network.ClientFactory.CreationException +import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.client.utils.IntentUtil +import com.nextcloud.model.ToolbarItem +import com.nextcloud.model.ToolbarStyle +import com.nextcloud.model.WorkerState +import com.nextcloud.model.WorkerState.DownloadFinished +import com.nextcloud.model.WorkerState.DownloadStarted +import com.nextcloud.model.WorkerState.OfflineOperationsCompleted +import com.nextcloud.model.WorkerState.UploadFinished +import com.nextcloud.model.WorkerStateLiveData +import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.extensions.isActive +import com.nextcloud.utils.extensions.lastFragment +import com.nextcloud.utils.extensions.logFileSize +import com.nextcloud.utils.fileNameValidator.FileNameValidator.checkFolderPath +import com.nextcloud.utils.view.FastScrollUtils +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.databinding.FilesBinding +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.SyncedFolderProvider +import com.owncloud.android.datamodel.VirtualFolderType +import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.files.RestoreFileVersionRemoteOperation +import com.owncloud.android.lib.resources.files.SearchRemoteOperation +import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation +import com.owncloud.android.operations.CopyFileOperation +import com.owncloud.android.operations.CreateFolderOperation +import com.owncloud.android.operations.DownloadType +import com.owncloud.android.operations.MoveFileOperation +import com.owncloud.android.operations.RefreshFolderOperation +import com.owncloud.android.operations.RemoveFileOperation +import com.owncloud.android.operations.RenameFileOperation +import com.owncloud.android.operations.SynchronizeFileOperation +import com.owncloud.android.operations.UploadFileOperation +import com.owncloud.android.syncadapter.FileSyncAdapter +import com.owncloud.android.ui.CompletionCallback +import com.owncloud.android.ui.asynctasks.CheckAvailableSpaceTask +import com.owncloud.android.ui.asynctasks.CheckAvailableSpaceTask.CheckAvailableSpaceListener +import com.owncloud.android.ui.asynctasks.FetchRemoteFileTask +import com.owncloud.android.ui.asynctasks.GetRemoteFileTask +import com.owncloud.android.ui.dialog.SendShareDialog +import com.owncloud.android.ui.dialog.SendShareDialog.SendShareDialogDownloader +import com.owncloud.android.ui.dialog.SortingOrderDialogFragment.OnSortingOrderListener +import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment +import com.owncloud.android.ui.dialog.TermsOfServiceDialog +import com.owncloud.android.ui.events.SearchEvent +import com.owncloud.android.ui.events.SyncEventFinished +import com.owncloud.android.ui.events.TokenPushEvent +import com.owncloud.android.ui.fragment.EmptyListState +import com.owncloud.android.ui.fragment.FileDetailFragment +import com.owncloud.android.ui.fragment.FileFragment +import com.owncloud.android.ui.fragment.GalleryFragment +import com.owncloud.android.ui.fragment.GroupfolderListFragment +import com.owncloud.android.ui.fragment.OCFileListFragment +import com.owncloud.android.ui.fragment.SearchType +import com.owncloud.android.ui.fragment.SharedListFragment +import com.owncloud.android.ui.fragment.TaskRetainerFragment +import com.owncloud.android.ui.fragment.UnifiedSearchFragment +import com.owncloud.android.ui.helpers.FileOperationsHelper +import com.owncloud.android.ui.helpers.UriUploader +import com.owncloud.android.ui.interfaces.TransactionInterface +import com.owncloud.android.ui.preview.PreviewImageActivity +import com.owncloud.android.ui.preview.PreviewImageFragment +import com.owncloud.android.ui.preview.PreviewMediaActivity +import com.owncloud.android.ui.preview.PreviewMediaFragment +import com.owncloud.android.ui.preview.PreviewMediaFragment.Companion.newInstance +import com.owncloud.android.ui.preview.PreviewTextFileFragment +import com.owncloud.android.ui.preview.PreviewTextFragment +import com.owncloud.android.ui.preview.PreviewTextStringFragment +import com.owncloud.android.ui.preview.pdf.PreviewPdfFragment.Companion.newInstance +import com.owncloud.android.utils.DataHolderUtil +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.ErrorMessageAdapter +import com.owncloud.android.utils.FileSortOrder +import com.owncloud.android.utils.MimeTypeUtil +import com.owncloud.android.utils.PermissionUtil +import com.owncloud.android.utils.PermissionUtil.requestExternalStoragePermission +import com.owncloud.android.utils.PermissionUtil.requestNotificationPermission +import com.owncloud.android.utils.PushUtils +import com.owncloud.android.utils.StringUtils +import com.owncloud.android.utils.theme.CapabilityUtils +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import java.io.File +import java.util.function.Supplier +import javax.inject.Inject + +/** + * Displays, what files the user has available in his Nextcloud. This is the main view. + */ +@Suppress( + "ComplexCondition", + "SpreadOperator", + "ForbiddenComment", + "ReturnCount", + "LargeClass", + "NestedBlockDepth", + "TooManyFunctions" +) +class FileDisplayActivity : + FileActivity(), + FileFragment.ContainerActivity, + OnEnforceableRefreshListener, + OnSortingOrderListener, + SendShareDialogDownloader, + Injectable { + private lateinit var binding: FilesBinding + + private var mSyncBroadcastReceiver: SyncBroadcastReceiver? = null + private var mUploadFinishReceiver: UploadFinishReceiver? = null + private var mDownloadFinishReceiver: DownloadFinishReceiver? = null + private var mLastSslUntrustedServerResult: RemoteOperationResult<*>? = null + + private var mWaitingToPreview: OCFile? = null + + private var mSyncInProgress: Boolean = false + set(value) { + field = value + setBackgroundText() + } + + private var mWaitingToSend: OCFile? = null + + private var mDrawerMenuItemstoShowHideList: MutableCollection? = null + + private var searchQuery: String? = "" + private var searchOpen = false + + private var searchView: SearchView? = null + private var mPlayerConnection: PlayerServiceConnection? = null + private var lastDisplayedAccountName: String? = null + + @Inject + lateinit var localBroadcastManager: LocalBroadcastManager + + @Inject + lateinit var preferences: AppPreferences + + @Inject + lateinit var appInfo: AppInfo + + @Inject + lateinit var inAppReviewHelper: InAppReviewHelper + + @Inject + lateinit var fastScrollUtils: FastScrollUtils + + @Inject + lateinit var asyncRunner: AsyncRunner + + @Inject + lateinit var clock: Clock + + @Inject + lateinit var syncedFolderProvider: SyncedFolderProvider + + /** + * Indicates whether the downloaded file should be previewed immediately. Since `FileDownloadWorker` can be + * triggered from multiple sources, this helps determine if an automatic preview is needed after download. + */ + private var fileIDForImmediatePreview: Long = -1 + + fun setFileIDForImmediatePreview(fileIDForImmediatePreview: Long) { + this.fileIDForImmediatePreview = fileIDForImmediatePreview + } + + @SuppressLint("UnsafeIntentLaunch") + override fun onCreate(savedInstanceState: Bundle?) { + Log_OC.v(TAG, "onCreate() start") + // Set the default theme to replace the launch screen theme. + setTheme(R.style.Theme_ownCloud_Toolbar_Drawer) + + super.onCreate(savedInstanceState) + lastDisplayedAccountName = preferences.lastDisplayedAccountName + + intent?.let { + handleCommonIntents(it) + handleAccountSwitchIntent(it) + } + + loadSavedInstanceState(savedInstanceState) + + /** USER INTERFACE */ + initLayout() + initUI() + initTaskRetainerFragment() + + // Restoring after UI has been inflated. + if (savedInstanceState != null) { + showSortListGroup(savedInstanceState.getBoolean(KEY_IS_SORT_GROUP_VISIBLE)) + } + + mPlayerConnection = PlayerServiceConnection(this) + + checkStoragePath() + + initSyncBroadcastReceiver() + observeWorkerState() + startMetadataSyncForRoot() + } + + private fun loadSavedInstanceState(savedInstanceState: Bundle?) { + if (savedInstanceState != null) { + mWaitingToPreview = + savedInstanceState.getParcelableArgument(KEY_WAITING_TO_PREVIEW, OCFile::class.java) + mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS) + mWaitingToSend = savedInstanceState.getParcelableArgument(KEY_WAITING_TO_SEND, OCFile::class.java) + searchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY) + searchOpen = savedInstanceState.getBoolean(KEY_IS_SEARCH_OPEN, false) + } else { + mWaitingToPreview = null + mSyncInProgress = false + mWaitingToSend = null + } + } + + private fun initLayout() { + // Inflate and set the layout view + binding = FilesBinding.inflate(layoutInflater) + setContentView(binding.getRoot()) + } + + private fun initUI() { + setupHomeSearchToolbarWithSortAndListButtons() + mMenuButton.setOnClickListener { v: View? -> openDrawer() } + mSwitchAccountButton.setOnClickListener { v: View? -> showManageAccountsDialog() } + mNotificationButton.setOnClickListener { v: View? -> startActivity(NotificationsActivity::class.java) } + fastScrollUtils.fixAppBarForFastScroll(binding.appbar.appbar, binding.rootLayout) + } + + private fun initTaskRetainerFragment() { + // Init Fragment without UI to retain AsyncTask across configuration changes + val fm = supportFragmentManager + var taskRetainerFragment = + fm.findFragmentByTag(TaskRetainerFragment.FTAG_TASK_RETAINER_FRAGMENT) as TaskRetainerFragment? + if (taskRetainerFragment == null) { + taskRetainerFragment = TaskRetainerFragment() + fm.beginTransaction().add(taskRetainerFragment, TaskRetainerFragment.FTAG_TASK_RETAINER_FRAGMENT).commit() + } // else, Fragment already created and retained across configuration change + } + + private fun checkStoragePath() { + val newStorage = Environment.getExternalStorageDirectory().absolutePath + val storagePath = preferences.getStoragePath(newStorage) + if (!preferences.isStoragePathValid() && !File(storagePath).exists()) { + // falling back to default + preferences.setStoragePath(newStorage) + preferences.setStoragePathValid() + MainApp.setStoragePath(newStorage) + + try { + val builder = MaterialAlertDialogBuilder(this, R.style.Theme_ownCloud_Dialog) + .setTitle(R.string.wrong_storage_path) + .setMessage(R.string.wrong_storage_path_desc) + .setPositiveButton( + R.string.dialog_close + ) { dialog: DialogInterface?, which: Int -> dialog?.dismiss() } + .setIcon(R.drawable.ic_settings) + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(applicationContext, builder) + + builder.create().show() + } catch (e: BadTokenException) { + Log_OC.e(TAG, "Error showing wrong storage info, so skipping it: " + e.message) + } + } + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val fragment = + supportFragmentManager.findFragmentByTag( + PermissionUtil.PERMISSION_CHOICE_DIALOG_TAG + ) as StoragePermissionDialogFragment? + if (fragment != null) { + val dialog = fragment.dialog + + if (dialog != null && dialog.isShowing) { + dialog.dismiss() + supportFragmentManager.beginTransaction().remove(fragment).commitNowAllowingStateLoss() + requestExternalStoragePermission(this, viewThemeUtils) + } + } + } + } + + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + + // handle notification permission on API level >= 33 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // request notification permission first and then prompt for storage permissions + // storage permissions handled in onRequestPermissionsResult + requestNotificationPermission(this) + } else { + requestExternalStoragePermission(this, viewThemeUtils) + } + + if (intent.getParcelableArgument( + OCFileListFragment.SEARCH_EVENT, + SearchEvent::class.java + ) != null + ) { + switchToSearchFragment(savedInstanceState) + setupDrawer() + } else { + createMinFragments(savedInstanceState) + } + + upgradeNotificationForInstantUpload() + checkOutdatedServer() + checkNotifications() + } + + /** + * For Android 7+. Opens a pop up info for the new instant upload and disabled the old instant upload. + */ + private fun upgradeNotificationForInstantUpload() { + // check for Android 6+ if legacy instant upload is activated --> disable + show info + if (preferences.instantPictureUploadEnabled() || preferences.instantVideoUploadEnabled()) { + preferences.removeLegacyPreferences() + // show info pop-up + MaterialAlertDialogBuilder(this, R.style.Theme_ownCloud_Dialog).setTitle(R.string.drawer_synced_folders) + .setMessage( + R.string.synced_folders_new_info + ).setPositiveButton( + R.string.drawer_open + ) { dialog: DialogInterface?, which: Int -> + // show instant upload + val syncedFoldersIntent = Intent(applicationContext, SyncedFoldersActivity::class.java) + dialog?.dismiss() + startActivity(syncedFoldersIntent) + }.setNegativeButton( + R.string.drawer_close + ) { dialog: DialogInterface?, which: Int -> dialog?.dismiss() } + .setIcon( + R.drawable.nav_synced_folders + ).show() + } + } + + private fun checkOutdatedServer() { + val user = getUser() + // show outdated warning + if (user.isPresent && + CapabilityUtils.checkOutdatedWarning( + getResources(), + user.get().server.version, + capabilities.extendedSupport.isTrue, + capabilities.hasValidSubscription.isTrue + ) + ) { + DisplayUtils.showServerOutdatedSnackbar(this, Snackbar.LENGTH_LONG) + } + } + + private fun checkNotifications() { + lifecycleScope.launch(Dispatchers.IO) { + try { + val result = GetNotificationsRemoteOperation() + .execute(clientFactory.createNextcloudClient(accountManager.user)) + + if (result.isSuccess && result.getResultData()?.isEmpty() == false) { + runOnUiThread { mNotificationButton.visibility = View.VISIBLE } + } else { + runOnUiThread { mNotificationButton.visibility = View.GONE } + } + } catch (_: CreationException) { + Log_OC.e(TAG, "Could not fetch notifications!") + } + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + when (requestCode) { + // handle notification permission on API level >= 33 + PermissionUtil.PERMISSIONS_POST_NOTIFICATIONS -> + // dialogue was dismissed -> prompt for storage permissions + requestExternalStoragePermission(this, viewThemeUtils) + + // If request is cancelled, result arrays are empty. + PermissionUtil.PERMISSIONS_EXTERNAL_STORAGE -> + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // permission was granted + EventBus.getDefault().post(TokenPushEvent()) + // toggle on is save since this is the only scenario this code gets accessed + } + + // If request is cancelled, result arrays are empty. + PermissionUtil.PERMISSIONS_CAMERA -> + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // permission was granted + getOCFileListFragmentFromFile(object : TransactionInterface { + override fun onOCFileListFragmentComplete(fragment: OCFileListFragment) { + fragment.directCameraUpload() + } + }) + } + + else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } + } + + private fun switchToSearchFragment(savedInstanceState: Bundle?) { + if (savedInstanceState == null) { + val listOfFiles = OCFileListFragment() + val args = Bundle() + + args.putParcelable( + OCFileListFragment.SEARCH_EVENT, + intent + .getParcelableArgument( + OCFileListFragment.SEARCH_EVENT, + SearchEvent::class.java + ) + ) + args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true) + + listOfFiles.setArguments(args) + val transaction = supportFragmentManager.beginTransaction() + transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES) + transaction.commit() + } else { + supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FILES) + } + } + + private fun createMinFragments(savedInstanceState: Bundle?) { + if (savedInstanceState == null) { + val listOfFiles = OCFileListFragment() + val args = Bundle() + args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true) + listOfFiles.setArguments(args) + val transaction = supportFragmentManager.beginTransaction() + transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES) + transaction.commit() + } else { + supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FILES) + } + } + + private fun initFragments() { + /** First fragment */ + val listOfFiles = this.listOfFilesFragment + if (listOfFiles != null && TextUtils.isEmpty(searchQuery)) { + listOfFiles.listDirectory(getCurrentDir(), file, MainApp.isOnlyOnDevice(), false) + } else { + Log_OC.e(TAG, "Still have a chance to lose the initialization of list fragment >(") + } + + /** reset views */ + resetTitleBarAndScrolling() + } + + // region Handle Intents + @SuppressLint("UnsafeIntentLaunch") + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + setIntent(intent) + handleCommonIntents(intent) + handleSpecialIntents(intent) + handleRestartIntent(intent) + } + + private fun handleSpecialIntents(intent: Intent) { + val action = intent.action + + when { + ACTION_DETAILS.equals(action, ignoreCase = true) -> { + val file = getFileFromIntent(intent) + setFile(file) + showDetails(file) + } + + Intent.ACTION_SEARCH == action -> handleSearchIntent(intent) + + ALL_FILES == action -> { + Log_OC.d(this, "Switch to oc file fragment") + menuItemId = R.id.nav_all_files + leftFragment = OCFileListFragment() + supportFragmentManager.executePendingTransactions() + browseToRoot() + } + + LIST_GROUPFOLDERS == action -> { + Log_OC.d(this, "Switch to list groupfolders fragment") + menuItemId = R.id.nav_groupfolders + leftFragment = GroupfolderListFragment() + supportFragmentManager.executePendingTransactions() + } + } + } + + @SuppressLint("UnsafeIntentLaunch") + private fun handleCommonIntents(intent: Intent) { + when (intent.action) { + Intent.ACTION_VIEW -> handleOpenFileViaIntent(intent) + OPEN_FILE -> { + onOpenFileIntent(intent) + } + } + } + + private fun handleRestartIntent(intent: Intent) { + if (intent.action != RESTART) { + return + } + + finish() + startActivity(intent) + } + + private fun handleAccountSwitchIntent(intent: Intent) { + if (intent.action != RESTART) { + return + } + + val accountName = accountManager.user.accountName + val message = getString(R.string.logged_in_as) + val snackBarMessage = String.format(message, accountName) + DisplayUtils.showSnackMessage(this, snackBarMessage) + } + + private fun handleSearchIntent(intent: Intent) { + val searchEvent = intent.getParcelableArgument( + OCFileListFragment.SEARCH_EVENT, + SearchEvent::class.java + ) ?: return + + when (searchEvent.searchType) { + SearchRemoteOperation.SearchType.PHOTO_SEARCH -> { + Log_OC.d(this, "Switch to photo search fragment") + val bundle = Bundle().apply { + putParcelable(OCFileListFragment.SEARCH_EVENT, searchEvent) + } + leftFragment = GalleryFragment().apply { + arguments = bundle + } + } + + SearchRemoteOperation.SearchType.SHARED_FILTER -> { + Log_OC.d(this, "Switch to shared fragment") + val bundle = Bundle().apply { + putParcelable(OCFileListFragment.SEARCH_EVENT, searchEvent) + } + leftFragment = SharedListFragment().apply { + arguments = bundle + } + } + + else -> { + Log_OC.d(this, "Switch to oc file search fragment") + val bundle = Bundle().apply { + putParcelable(OCFileListFragment.SEARCH_EVENT, searchEvent) + } + leftFragment = OCFileListFragment().apply { + arguments = bundle + } + } + } + } + // endregion + + private fun onOpenFileIntent(intent: Intent) { + val file = getFileFromIntent(intent) + if (file == null) { + Log_OC.e(TAG, "Can't open file intent, file is null") + return + } + + val currentFragment = leftFragment + + if (currentFragment == null) { + Log_OC.e(TAG, "Can't open file intent, left fragment is null") + return + } + + val fileListFragment: OCFileListFragment = when { + currentFragment is OCFileListFragment && currentFragment !is GalleryFragment -> { + currentFragment + } + + else -> { + Log_OC.w( + TAG, + "Left fragment is not a valid OCFileListFragment " + + "(was ${currentFragment::class.simpleName}). " + + "Replacing with OCFileListFragment." + ) + val newFragment = OCFileListFragment() + setLeftFragment(newFragment, false) + setupHomeSearchToolbarWithSortAndListButtons() + newFragment + } + } + + // Post to main thread to ensure fragment is fully attached before interacting + Handler(Looper.getMainLooper()).post { + fileListFragment.onItemClicked(file) + } + } + + private fun setLeftFragment(fragment: Fragment?, showSortListGroup: Boolean) { + if (fragment == null) { + return + } + + prepareFragmentBeforeCommit(showSortListGroup) + commitFragment( + fragment, + object : CompletionCallback { + override fun onComplete(isFragmentCommitted: Boolean) { + Log_OC.d( + TAG, + "Left fragment committed: $isFragmentCommitted" + ) + } + } + ) + } + + private fun prepareFragmentBeforeCommit(showSortListGroup: Boolean) { + searchView?.post { searchView?.setQuery(searchQuery, true) } + setDrawerIndicatorEnabled(false) + + // clear the subtitle while navigating to any other screen from Media screen + clearToolbarSubtitle() + + showSortListGroup(showSortListGroup) + } + + private fun commitFragment(fragment: Fragment, callback: CompletionCallback) { + val fragmentManager = supportFragmentManager + if (this.isActive() && !fragmentManager.isDestroyed) { + val transaction = fragmentManager.beginTransaction() + transaction.addToBackStack(null) + transaction.replace(R.id.left_fragment_container, fragment, TAG_LIST_OF_FILES) + transaction.commit() + callback.onComplete(true) + } else { + callback.onComplete(false) + } + } + + private fun getOCFileListFragmentFromFile(transaction: TransactionInterface) { + val leftFragment = this.leftFragment + + if (leftFragment is OCFileListFragment) { + transaction.onOCFileListFragmentComplete(leftFragment) + return + } + + val listOfFiles = OCFileListFragment() + val args = Bundle() + args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, true) + listOfFiles.setArguments(args) + + runOnUiThread { + val fm = supportFragmentManager + if (!fm.isStateSaved && !fm.isDestroyed) { + prepareFragmentBeforeCommit(true) + commitFragment( + listOfFiles, + object : CompletionCallback { + override fun onComplete(value: Boolean) { + if (value) { + Log_OC.d(TAG, "OCFileListFragment committed, executing pending transaction") + fm.executePendingTransactions() + transaction.onOCFileListFragmentComplete(listOfFiles) + } else { + Log_OC.d( + TAG, + "OCFileListFragment not committed, skipping executing " + + "pending transaction" + ) + } + } + } + ) + } + } + } + + fun showFileActions(file: OCFile?) { + dismissLoadingDialog() + getOCFileListFragmentFromFile(object : TransactionInterface { + override fun onOCFileListFragmentComplete(fragment: OCFileListFragment) { + browseUp(fragment) + fragment.onOverflowIconClicked(file, null) + } + }) + } + + var leftFragment: Fragment? + get() = supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FILES) + + /** + * Replaces the first fragment managed by the activity with the received as a parameter. + * + * @param fragment New Fragment to set. + */ + private set(fragment) { + setLeftFragment(fragment, true) + } + + @get:Deprecated("") + val listOfFilesFragment: OCFileListFragment? + get() { + val listOfFiles = + supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FILES) + if (listOfFiles is OCFileListFragment) { + return listOfFiles + } + Log_OC.e(TAG, "Access to unexisting list of files fragment") + return null + } + + protected fun resetTitleBarAndScrolling() { + updateActionBarTitleAndHomeButton(null) + resetScrolling(true) + } + + fun updateListOfFilesFragment(fromSearch: Boolean) { + val fileListFragment = this.listOfFilesFragment + fileListFragment?.listDirectory(MainApp.isOnlyOnDevice(), fromSearch) + } + + fun resetSearchView() { + val fileListFragment = this.listOfFilesFragment + fileListFragment?.isSearchFragment = false + } + + protected fun refreshDetailsFragmentIfVisible( + downloadEvent: String, + downloadedRemotePath: String, + success: Boolean + ) { + val leftFragment = this.leftFragment + if (leftFragment is FileDetailFragment) { + val waitedPreview = mWaitingToPreview != null && mWaitingToPreview?.remotePath == downloadedRemotePath + val fileInFragment = leftFragment.file + if (fileInFragment != null && downloadedRemotePath != fileInFragment.remotePath) { + // the user browsed to other file ; forget the automatic preview + mWaitingToPreview = null + } else if (downloadEvent == getDownloadAddedMessage()) { + // grant that the details fragment updates the progress bar + leftFragment.listenForTransferProgress() + leftFragment.updateFileDetails(true, false) + } else if (downloadEvent == getDownloadFinishMessage()) { + // update the details panel + var detailsFragmentChanged = false + if (waitedPreview) { + if (success) { + // update the file from database, for the local storage path + mWaitingToPreview = mWaitingToPreview?.fileId?.let { storageManager.getFileById(it) } + + if (PreviewMediaActivity.Companion.canBePreviewed(mWaitingToPreview)) { + mWaitingToPreview?.let { + startMediaPreview(it, 0, true, true, true, true) + detailsFragmentChanged = true + } + } else if (MimeTypeUtil.isVCard(mWaitingToPreview?.mimeType)) { + startContactListFragment(mWaitingToPreview) + detailsFragmentChanged = true + } else if (PreviewTextFileFragment.canBePreviewed(mWaitingToPreview)) { + startTextPreview(mWaitingToPreview, true) + detailsFragmentChanged = true + } else if (MimeTypeUtil.isPDF(mWaitingToPreview)) { + mWaitingToPreview?.let { + startPdfPreview(it) + detailsFragmentChanged = true + } + } else { + fileOperationsHelper.openFile(mWaitingToPreview) + } + } + mWaitingToPreview = null + } + if (!detailsFragmentChanged) { + leftFragment.updateFileDetails(false, success) + } + } + } + } + + override fun onPrepareOptionsMenu(menu: Menu?): Boolean { + if (mDrawerMenuItemstoShowHideList != null) { + val drawerOpen = isDrawerOpen + for (menuItem in mDrawerMenuItemstoShowHideList) { + menuItem.isVisible = !drawerOpen + } + } + + return super.onPrepareOptionsMenu(menu) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val inflater = menuInflater + inflater.inflate(R.menu.activity_file_display, menu) + + menu.findItem(R.id.action_select_all).isVisible = false + val searchMenuItem = menu.findItem(R.id.action_search) + searchView = MenuItemCompat.getActionView(searchMenuItem) as SearchView? + searchMenuItem.isVisible = false + mSearchText.setOnClickListener { v: View? -> + showSearchView() + searchView?.isIconified = false + } + + searchView?.let { viewThemeUtils.androidx.themeToolbarSearchView(it) } + + // populate list of menu items to show/hide when drawer is opened/closed + mDrawerMenuItemstoShowHideList = ArrayList(1) + mDrawerMenuItemstoShowHideList?.add(searchMenuItem) + + // focus the SearchView + if (!TextUtils.isEmpty(searchQuery)) { + searchView?.post { + searchView?.isIconified = false + searchView?.setQuery(searchQuery, true) + searchView?.clearFocus() + } + } + + val mSearchEditFrame = searchView?.findViewById(androidx.appcompat.R.id.search_edit_frame) + + searchView?.setOnCloseListener { + if (TextUtils.isEmpty(searchView?.query.toString())) { + searchView?.onActionViewCollapsed() + setDrawerIndicatorEnabled(isDrawerIndicatorAvailable) // order matters + supportActionBar?.setDisplayHomeAsUpEnabled(true) + mDrawerToggle.syncState() + + val ocFileListFragment = this.listOfFilesFragment + if (ocFileListFragment != null) { + ocFileListFragment.isSearchFragment = false + ocFileListFragment.refreshDirectory() + } + } else { + searchView?.post { searchView?.setQuery("", true) } + } + true + } + + val vto = mSearchEditFrame?.viewTreeObserver + vto?.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { + var oldVisibility: Int = -1 + + override fun onGlobalLayout() { + val currentVisibility = mSearchEditFrame.visibility + + if (currentVisibility != oldVisibility) { + if (currentVisibility == View.VISIBLE) { + setDrawerIndicatorEnabled(false) + } + + oldVisibility = currentVisibility + } + } + }) + + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + var retval = true + + val itemId = item.itemId + + if (itemId == android.R.id.home) { + if (!isDrawerOpen && + !isSearchOpen() && + isRoot(getCurrentDir()) && + this.leftFragment is OCFileListFragment + ) { + openDrawer() + } else { + onBackPressed() + } + } else if (itemId == R.id.action_select_all) { + val fragment = this.listOfFilesFragment + fragment?.selectAllFiles(true) + } else { + retval = super.onOptionsItemSelected(item) + } + + return retval + } + + /** + * Called, when the user selected something for uploading + */ + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (data != null && + requestCode == REQUEST_CODE__SELECT_CONTENT_FROM_APPS && + (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE) + ) { + requestUploadOfContentFromApps(data, resultCode) + } else if (data != null && + requestCode == REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM && + ( + resultCode == RESULT_OK || + resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE || + resultCode == UploadFilesActivity.RESULT_OK_AND_DO_NOTHING || + resultCode == UploadFilesActivity.RESULT_OK_AND_DELETE + ) + ) { + requestUploadOfFilesFromFileSystem(data, resultCode) + } else if (( + requestCode == REQUEST_CODE__UPLOAD_FROM_CAMERA || + requestCode == REQUEST_CODE__UPLOAD_FROM_VIDEO_CAMERA + ) && + (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_DELETE) + ) { + CheckAvailableSpaceTask( + object : CheckAvailableSpaceListener { + override fun onCheckAvailableSpaceStart() { + Log_OC.d(this, "onCheckAvailableSpaceStart") + } + + override fun onCheckAvailableSpaceFinish( + hasEnoughSpaceAvailable: Boolean, + vararg filesToUpload: String? + ) { + Log_OC.d(this, "onCheckAvailableSpaceFinish") + + if (hasEnoughSpaceAvailable) { + val file = File(filesToUpload[0]) + val renamedFile = if (requestCode == REQUEST_CODE__UPLOAD_FROM_CAMERA) { + File(file.parent + OCFile.PATH_SEPARATOR + FileOperationsHelper.getCapturedImageName()) + } else { + File(file.parent + OCFile.PATH_SEPARATOR + FileOperationsHelper.getCapturedVideoName()) + } + + if (!file.renameTo(renamedFile)) { + DisplayUtils.showSnackMessage( + this@FileDisplayActivity, + R.string.error_uploading_direct_camera_upload + ) + return + } + + requestUploadOfFilesFromFileSystem( + renamedFile.parentFile?.absolutePath, + arrayOf(renamedFile.absolutePath), + FileUploadWorker.LOCAL_BEHAVIOUR_DELETE + ) + } + } + }, + *arrayOf( + FileOperationsHelper.createCameraFile( + this@FileDisplayActivity, + requestCode == REQUEST_CODE__UPLOAD_FROM_VIDEO_CAMERA + ).absolutePath + ) + ).execute() + } else if (requestCode == REQUEST_CODE__MOVE_OR_COPY_FILES && resultCode == RESULT_OK) { + exitSelectionMode() + } else { + super.onActivityResult(requestCode, resultCode, data) + } + } + + private fun exitSelectionMode() { + val ocFileListFragment = this.listOfFilesFragment + ocFileListFragment?.exitSelectionMode() + } + + private fun requestUploadOfFilesFromFileSystem(data: Intent, resultCode: Int) { + val filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES) ?: return + val basePath = data.getStringExtra(UploadFilesActivity.LOCAL_BASE_PATH) + requestUploadOfFilesFromFileSystem(basePath, filePaths, resultCode) + } + + private fun getRemotePaths(directory: String?, filePaths: Array, localBasePath: String): Array = + Array(filePaths.size) { j -> + val relativePath = StringUtils.removePrefix(filePaths[j], localBasePath) + (directory ?: "") + relativePath + } + + private fun requestUploadOfFilesFromFileSystem(localBasePath: String?, filePaths: Array, resultCode: Int) { + var localBasePath = localBasePath + if (localBasePath != null) { + if (!localBasePath.endsWith("/")) { + localBasePath = "$localBasePath/" + } + + val remotePathBase = getCurrentDir().remotePath + val decryptedRemotePaths = getRemotePaths(remotePathBase, filePaths, localBasePath) + + val behaviour = when (resultCode) { + UploadFilesActivity.RESULT_OK_AND_MOVE -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE + UploadFilesActivity.RESULT_OK_AND_DELETE -> FileUploadWorker.LOCAL_BEHAVIOUR_DELETE + else -> FileUploadWorker.LOCAL_BEHAVIOUR_FORGET + } + + connectivityService.isNetworkAndServerAvailable { result: Boolean? -> + if (result == true) { + val isValidFolderPath = checkFolderPath(remotePathBase, capabilities, this) + if (!isValidFolderPath) { + DisplayUtils.showSnackMessage( + this, + R.string.file_name_validator_error_contains_reserved_names_or_invalid_characters + ) + return@isNetworkAndServerAvailable + } + + FileUploadHelper.Companion.instance().uploadNewFiles( + user.orElseThrow( + Supplier { RuntimeException() } + ), + filePaths, + decryptedRemotePaths, + behaviour, + true, + UploadFileOperation.CREATED_BY_USER, + false, + false, + NameCollisionPolicy.ASK_USER + ) + } else { + fileDataStorageManager.addCreateFileOfflineOperation(filePaths, decryptedRemotePaths) + } + } + } else { + Log_OC.d(TAG, "User clicked on 'Update' with no selection") + DisplayUtils.showSnackMessage(this, R.string.filedisplay_no_file_selected) + } + } + + private fun requestUploadOfContentFromApps(contentIntent: Intent, resultCode: Int) { + val streamsToUpload = ArrayList() + + if (contentIntent.clipData != null && (contentIntent.clipData?.itemCount ?: 0) > 0) { + for (i in 0..<(contentIntent.clipData?.itemCount ?: 0)) { + streamsToUpload.add(contentIntent.clipData?.getItemAt(i)?.uri) + } + } else { + streamsToUpload.add(contentIntent.data) + } + + val behaviour = + if (resultCode == + UploadFilesActivity.RESULT_OK_AND_MOVE + ) { + FileUploadWorker.LOCAL_BEHAVIOUR_MOVE + } else { + FileUploadWorker.LOCAL_BEHAVIOUR_COPY + } + + val currentDir = getCurrentDir() + val remotePath = if (currentDir != null) currentDir.remotePath else OCFile.ROOT_PATH + + val uploader = UriUploader( + this, + streamsToUpload, + remotePath, + user.orElseThrow( + Supplier { RuntimeException() } + ), + behaviour, + false, // Not show waiting dialog while file is being copied from private storage + null // Not needed copy temp task listener + ) + + uploader.uploadUris() + } + + private fun isSearchOpen(): Boolean { + if (searchView == null) { + return false + } else { + val mSearchEditFrame = searchView?.findViewById(androidx.appcompat.R.id.search_edit_frame) + return mSearchEditFrame != null && mSearchEditFrame.isVisible + } + } + + private val isRootDirectory: Boolean + get() { + val currentDir = getCurrentDir() + return (currentDir == null || currentDir.parentId == FileDataStorageManager.ROOT_PARENT_ID.toLong()) + } + + /* + * BackPressed priority/hierarchy: + * 1. close search view if opened + * 2. close drawer if opened + * 3. if it is OCFileListFragment and it's in Root -> (finish Activity) or it's not Root -> (browse up) + * 4. otherwise pop up the fragment and sortGroup view visibility and call super.onBackPressed() + */ + @SuppressFBWarnings("ITC_INHERITANCE_TYPE_CHECKING") // TODO Apply fail fast principle + override fun onBackPressed() { + if (isSearchOpen()) { + resetSearchAction() + return + } + + if (isDrawerOpen) { + super.onBackPressed() + return + } + + if (this.leftFragment is OCFileListFragment) { + if (isRoot(getCurrentDir())) { + finish() + } else { + browseUp(leftFragment as OCFileListFragment) + } + } else { + popBack() + } + } + + private fun browseUp(listOfFiles: OCFileListFragment) { + listOfFiles.onBrowseUp() + val currentFile = listOfFiles.currentFile + + file = currentFile + + currentFile?.let { + listOfFiles.setFabVisible(currentFile.canCreateFileAndFolder()) + listOfFiles.registerFabListener() + } + + resetTitleBarAndScrolling() + configureToolbar() + startMetadataSyncForCurrentDir() + } + + private fun resetSearchAction() { + val leftFragment = this.leftFragment + if (!isSearchOpen() || searchView == null) { + return + } + + searchView?.setQuery("", true) + searchView?.onActionViewCollapsed() + searchView?.clearFocus() + + if (isRoot(getCurrentDir()) && leftFragment is OCFileListFragment) { + // Remove the list to the original state + + val listOfHiddenFiles = leftFragment.adapter.listOfHiddenFiles + leftFragment.performSearch("", listOfHiddenFiles, true) + + hideSearchView(getCurrentDir()) + setDrawerIndicatorEnabled(isDrawerIndicatorAvailable) + } + + if (leftFragment is UnifiedSearchFragment) { + showSortListGroup(false) + super.onBackPressed() + } + } + + /** + * Use this method when want to pop the fragment on back press. It resets Scrolling (See + * [with true][.resetScrolling] and pop the visibility for sortListGroup (See + * [with false][.showSortListGroup]. At last call to super.onBackPressed() + */ + private fun popBack() { + binding.fabMain.setImageResource(R.drawable.ic_plus) + resetScrolling(true) + showSortListGroup(false) + super.onBackPressed() + } + + override fun onSaveInstanceState(outState: Bundle) { + // responsibility of restore is preferred in onCreate() before than in + // onRestoreInstanceState when there are Fragments involved + super.onSaveInstanceState(outState) + mWaitingToPreview.logFileSize(TAG) + outState.putParcelable(KEY_WAITING_TO_PREVIEW, mWaitingToPreview) + outState.putBoolean(KEY_SYNC_IN_PROGRESS, mSyncInProgress) + // outState.putBoolean(FileDisplayActivity.KEY_REFRESH_SHARES_IN_PROGRESS, + // mRefreshSharesInProgress); + outState.putParcelable(KEY_WAITING_TO_SEND, mWaitingToSend) + if (searchView != null) { + outState.putBoolean(KEY_IS_SEARCH_OPEN, searchView?.isIconified == false) + } + outState.putString(KEY_SEARCH_QUERY, searchQuery) + outState.putBoolean(KEY_IS_SORT_GROUP_VISIBLE, sortListGroupVisibility()) + Log_OC.v(TAG, "onSaveInstanceState() end") + } + + override fun onResume() { + Log_OC.v(TAG, "onResume() start") + super.onResume() + isFileDisplayActivityResumed = true + + // Instead of onPostCreate, starting the loading in onResume for children fragments + val leftFragment = this.leftFragment + + // Listen for sync messages + if (leftFragment !is OCFileListFragment || !leftFragment.isSearchFragment) { + initSyncBroadcastReceiver() + } + + if (leftFragment !is OCFileListFragment) { + if (leftFragment is FileFragment) { + super.updateActionBarTitleAndHomeButton(leftFragment.file) + } + return + } + + val ocFileListFragment = leftFragment + syncAndUpdateFolder(ignoreETag = true, ignoreFocus = true) + + // Try to get the OCFile from the intent, if one was provided when launching this activity. + // 'file' comes from the FileActivity base class and represents the currently opened file or folder. + // We update it only when a valid file is found in the intent. + val startFile = intent?.let { getFileFromIntent(it) }?.also { + file = it + } + + // refresh list of files + if (searchView != null && !TextUtils.isEmpty(searchQuery)) { + searchView?.setQuery(searchQuery, false) + } else if (!ocFileListFragment.isSearchFragment && startFile == null) { + updateListOfFilesFragment(false) + ocFileListFragment.registerFabListener() + } else { + ocFileListFragment.listDirectory(startFile, false, false) + updateActionBarTitleAndHomeButton(startFile) + } + + // Listen for upload messages + val uploadIntentFilter = IntentFilter(getUploadFinishMessage()) + mUploadFinishReceiver = UploadFinishReceiver() + localBroadcastManager.registerReceiver(mUploadFinishReceiver!!, uploadIntentFilter) + + // Listen for download messages + val downloadIntentFilter = IntentFilter(getDownloadAddedMessage()) + downloadIntentFilter.addAction(getDownloadFinishMessage()) + mDownloadFinishReceiver = DownloadFinishReceiver() + mDownloadFinishReceiver?.let { + localBroadcastManager.registerReceiver(it, downloadIntentFilter) + } + + configureToolbar() + + // show in-app review dialog to user + inAppReviewHelper.showInAppReview(this) + + checkNotifications() + + Log_OC.v(TAG, "onResume() end") + + Handler(Looper.getMainLooper()).postDelayed({ + isFileDisplayActivityResumed = false + }, ON_RESUMED_RESET_DELAY) + } + + private fun getFileFromIntent(intent: Intent?): OCFile? = + intent.getParcelableArgument(EXTRA_FILE, OCFile::class.java) + ?: intent?.getStringExtra(EXTRA_FILE_REMOTE_PATH) + ?.let { fileDataStorageManager.getFileByDecryptedRemotePath(it) } + + private fun checkAndSetMenuItemId() { + if (MainApp.isOnlyPersonFiles()) { + menuItemId = R.id.nav_personal_files + } else if (MainApp.isOnlyOnDevice()) { + menuItemId = R.id.nav_on_device + } else if (menuItemId == Menu.NONE) { + menuItemId = R.id.nav_all_files + } + } + + private fun configureToolbar() { + checkAndSetMenuItemId() + setNavigationViewItemChecked() + val item = ToolbarItem.fromNavId(menuItemId) + when (item?.style) { + ToolbarStyle.SEARCH -> setupHomeSearchToolbarWithSortAndListButtons() + ToolbarStyle.PLAIN -> { + if (currentDir?.isRootDirectory == true) { + updateActionBarTitleAndHomeButtonByString(getString(item.titleId)) + } else { + setupToolbar() + } + } + else -> { + setupToolbar() + } + } + } + + fun initSyncBroadcastReceiver() { + if (mSyncBroadcastReceiver == null) { + val syncIntentFilter = IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START).apply { + addAction(FileSyncAdapter.EVENT_FULL_SYNC_END) + addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED) + addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED) + addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED) + } + + mSyncBroadcastReceiver = SyncBroadcastReceiver() + mSyncBroadcastReceiver?.let { + localBroadcastManager.registerReceiver(it, syncIntentFilter) + } + } + } + + override fun onPause() { + Log_OC.v(TAG, "onPause() start") + if (mSyncBroadcastReceiver != null) { + localBroadcastManager.unregisterReceiver(mSyncBroadcastReceiver!!) + mSyncBroadcastReceiver = null + } + if (mUploadFinishReceiver != null) { + localBroadcastManager.unregisterReceiver(mUploadFinishReceiver!!) + mUploadFinishReceiver = null + } + if (mDownloadFinishReceiver != null) { + localBroadcastManager.unregisterReceiver(mDownloadFinishReceiver!!) + mDownloadFinishReceiver = null + } + + super.onPause() + Log_OC.v(TAG, "onPause() end") + } + + override fun onSortingOrderChosen(selection: FileSortOrder?) { + val ocFileListFragment = this.listOfFilesFragment + ocFileListFragment?.sortFiles(selection) + } + + override fun downloadFile(file: OCFile?, packageName: String?, activityName: String?) { + if (packageName != null && activityName != null) { + startDownloadForSending(file, OCFileListFragment.DOWNLOAD_SEND, packageName, activityName) + } + } + + // region SyncBroadcastReceiver + private inner class SyncBroadcastReceiver : BroadcastReceiver() { + @SuppressLint("VisibleForTests") + override fun onReceive(context: Context?, intent: Intent) { + try { + val event = intent.action + Log_OC.d(TAG, "Received broadcast $event") + + // region EventData + val accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME) + val syncFolderRemotePath = intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH) + val id = intent.getStringExtra(FileSyncAdapter.EXTRA_RESULT) + val syncResult = DataHolderUtil.getInstance().retrieve(id) + val sameAccount = + account != null && accountName != null && accountName == account.name && storageManager != null + val fileListFragment: OCFileListFragment? = this@FileDisplayActivity.listOfFilesFragment + + // endregion + if (sameAccount) { + handleSyncEvent(event, syncFolderRemotePath, id, fileListFragment, syncResult) + } + + if (syncResult is RemoteOperationResult<*> && + syncResult.code == RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED + ) { + mLastSslUntrustedServerResult = syncResult + } + } catch (_: java.lang.RuntimeException) { + safelyDeleteResult(intent) + } finally { + mSyncInProgress = false + } + } + } + + // avoid app crashes after changing the serial id of RemoteOperationResult in owncloud library + // with broadcast notifications pending to process + private fun safelyDeleteResult(intent: Intent) { + try { + DataHolderUtil.getInstance().delete(intent.getStringExtra(FileSyncAdapter.EXTRA_RESULT)) + } catch (_: java.lang.RuntimeException) { + Log_OC.i(TAG, "Ignoring error deleting data") + } + } + + private fun handleSyncEvent( + event: String?, + syncFolderRemotePath: String?, + id: String?, + fileListFragment: OCFileListFragment?, + syncResult: Any? + ) { + if (FileSyncAdapter.EVENT_FULL_SYNC_START == event) { + mSyncInProgress = true + return + } + + var currentFile = if (file == null) null else storageManager.getFileByPath(file.remotePath) + val currentDir = + if (getCurrentDir() == null) null else storageManager.getFileByPath(getCurrentDir().remotePath) + val isSyncFolderRemotePathRoot = OCFile.ROOT_PATH == syncFolderRemotePath + + if (currentDir == null && !isSyncFolderRemotePathRoot) { + handleRemovedFolder(syncFolderRemotePath) + } else if (currentDir != null) { + currentFile = handleRemovedFileFromServer(currentFile, currentDir) + updateFileList(fileListFragment, currentDir, syncFolderRemotePath) + file = currentFile + } + + handleSyncResult(event, syncResult) + + DataHolderUtil.getInstance().delete(id) + + mSyncInProgress = + FileSyncAdapter.EVENT_FULL_SYNC_END != event && + RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED != event + Log_OC.d(TAG, "Setting progress visibility to $mSyncInProgress") + + handleScrollBehaviour(fileListFragment) + } + + private fun handleRemovedFileFromServer(currentFile: OCFile?, currentDir: OCFile?): OCFile? { + if (currentFile == null && !file.isFolder) { + resetTitleBarAndScrolling() + return currentDir + } + + return currentFile + } + + private fun handleRemovedFolder(syncFolderRemotePath: String?) { + DisplayUtils.showSnackMessage(this, R.string.sync_current_folder_was_removed, syncFolderRemotePath) + browseToRoot() + } + + private fun updateFileList( + ocFileListFragment: OCFileListFragment?, + currentDir: OCFile, + syncFolderRemotePath: String? + ) { + if (currentDir.remotePath != syncFolderRemotePath) { + return + } + + if (ocFileListFragment == null) { + return + } + + ocFileListFragment.listDirectory(currentDir, MainApp.isOnlyOnDevice(), false) + } + + private fun handleScrollBehaviour(ocFileListFragment: OCFileListFragment?) { + if (ocFileListFragment == null) { + return + } + + if (mSyncInProgress || ocFileListFragment.isLoading) { + return + } + + if (ocFileListFragment.isEmpty) { + lockScrolling() + return + } + + resetScrolling(false) + } + + private fun handleSyncResult(event: String?, syncResult: Any?) { + if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED != event || syncResult == null) { + return + } + + if (syncResult is RemoteOperationResult<*> && syncResult.isSuccess) { + hideInfoBox() + return + } + + handleFailedSyncResult(syncResult) + } + + private fun handleFailedSyncResult(syncResult: Any?) { + if (checkForRemoteOperationError(syncResult)) { + requestCredentialsUpdate() + } else { + handleNonCredentialSyncErrors(syncResult) + } + } + + private fun handleNonCredentialSyncErrors(syncResult: Any?) { + if (syncResult !is RemoteOperationResult<*>) { + return + } + + when (syncResult.code) { + RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED -> showUntrustedCertDialog(syncResult) + RemoteOperationResult.ResultCode.MAINTENANCE_MODE -> showInfoBox(R.string.maintenance_mode) + RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION -> showInfoBox(R.string.offline_mode) + RemoteOperationResult.ResultCode.HOST_NOT_AVAILABLE -> showInfoBox(R.string.host_not_available) + RemoteOperationResult.ResultCode.SIGNING_TOS_NEEDED -> showTermsOfServiceDialog() + else -> {} + } + } + + private fun showTermsOfServiceDialog() { + if (supportFragmentManager.findFragmentByTag(DIALOG_TAG_SHOW_TOS) == null) { + TermsOfServiceDialog().show(supportFragmentManager, DIALOG_TAG_SHOW_TOS) + } + } + + private fun checkForRemoteOperationError(syncResult: Any?): Boolean { + if (syncResult !is RemoteOperationResult<*>) { + return false + } + + return RemoteOperationResult.ResultCode.UNAUTHORIZED == syncResult.code || + (syncResult.isException && syncResult.exception is AuthenticatorException) + } + + /** + * Show a text message on screen view for notifying user if content is loading or folder is empty + */ + private fun setBackgroundText() { + val ocFileListFragment = listOfFilesFragment ?: return + connectivityService.isNetworkAndServerAvailable { result: Boolean? -> + when { + mSyncInProgress && result == true -> { + ocFileListFragment.setEmptyListMessage(EmptyListState.LOADING) + } + MainApp.isOnlyOnDevice() -> { + ocFileListFragment.setEmptyListMessage(EmptyListState.ONLY_ON_DEVICE) + } + result == true -> ocFileListFragment.setEmptyListMessage(SearchType.NO_SEARCH) + else -> ocFileListFragment.setEmptyListMessage(EmptyListState.OFFLINE_MODE) + } + } + } + + // endregion + /** + * Once the file upload has finished -> update view + */ + private inner class UploadFinishReceiver : BroadcastReceiver() { + /** + * Once the file upload has finished -> update view + * + * + * [BroadcastReceiver] to enable upload feedback in UI + */ + override fun onReceive(context: Context?, intent: Intent) { + val uploadedRemotePath = intent.getStringExtra(FileUploadWorker.EXTRA_REMOTE_PATH) + val accountName = intent.getStringExtra(FileUploadWorker.ACCOUNT_NAME) + val account = getAccount() + val sameAccount = accountName != null && account != null && accountName == account.name + val currentDir = getCurrentDir() + val isDescendant = + currentDir != null && uploadedRemotePath != null && uploadedRemotePath.startsWith(currentDir.remotePath) + + if (sameAccount && isDescendant) { + val linkedToRemotePath = intent.getStringExtra(FileUploadWorker.EXTRA_LINKED_TO_PATH) + if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { + updateListOfFilesFragment(false) + } + } + + val uploadWasFine = intent.getBooleanExtra(FileUploadWorker.EXTRA_UPLOAD_RESULT, false) + + var renamedInUpload = false + var sameFile = false + if (file != null) { + renamedInUpload = + file.remotePath == intent.getStringExtra(FileUploadWorker.EXTRA_OLD_REMOTE_PATH) + sameFile = file.remotePath == uploadedRemotePath || renamedInUpload + } + + if (sameAccount && sameFile && this@FileDisplayActivity.leftFragment is FileDetailFragment) { + val fileDetailFragment = leftFragment as FileDetailFragment + if (uploadWasFine) { + file = storageManager.getFileByPath(uploadedRemotePath) + } else { + // TODO remove upload progress bar after upload failed. + Log_OC.d(TAG, "Remove upload progress bar after upload failed") + } + if (renamedInUpload && !uploadedRemotePath.isNullOrBlank()) { + val newName = File(uploadedRemotePath).name + DisplayUtils.showSnackMessage( + this@FileDisplayActivity, + R.string.filedetails_renamed_in_upload_msg, + newName + ) + } + + if (uploadWasFine || file != null && file.fileExists()) { + fileDetailFragment.updateFileDetails(false, true) + } else { + onBackPressed() + } + + // Force the preview if the file is an image or text file + if (uploadWasFine) { + val ocFile = file + if (PreviewImageFragment.canBePreviewed(ocFile)) { + startImagePreview(file, true) + } else if (PreviewTextFileFragment.canBePreviewed(ocFile)) { + startTextPreview(ocFile, true) + } + // TODO what about other kind of previews? + } + } + } + + // TODO refactor this receiver, and maybe DownloadFinishReceiver; this method is duplicated :S + fun isAscendant(linkedToRemotePath: String): Boolean { + val currentDir = getCurrentDir() + return currentDir != null && currentDir.remotePath.startsWith(linkedToRemotePath) + } + } + + /** + * Class waiting for broadcast events from the [FileDownloadWorker] service. + * + * + * Updates the UI when a download is started or finished, provided that it is relevant for the current folder. + */ + private inner class DownloadFinishReceiver : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent) { + val sameAccount = isSameAccount(intent) + val downloadedRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_REMOTE_PATH) + val downloadBehaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR) + val isDescendant = isDescendant(downloadedRemotePath) + + if (sameAccount && isDescendant) { + val linkedToRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_LINKED_TO_PATH) + if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { + updateListOfFilesFragment(false) + } + + val intentAction = intent.action + if (intentAction != null && downloadedRemotePath != null) { + refreshDetailsFragmentIfVisible( + intentAction, + downloadedRemotePath, + intent.getBooleanExtra(FileDownloadWorker.EXTRA_DOWNLOAD_RESULT, false) + ) + } + } + + if (mWaitingToSend != null) { + // update file after downloading + mWaitingToSend = storageManager.getFileByRemoteId(mWaitingToSend?.remoteId) + if (mWaitingToSend != null && + mWaitingToSend?.isDown == true && + OCFileListFragment.DOWNLOAD_SEND == downloadBehaviour + ) { + val packageName = intent.getStringExtra(SendShareDialog.PACKAGE_NAME) ?: return + val activityName = intent.getStringExtra(SendShareDialog.ACTIVITY_NAME) ?: return + sendDownloadedFile(packageName, activityName) + } + } + + if (mWaitingToPreview != null) { + mWaitingToPreview = storageManager.getFileByRemoteId(mWaitingToPreview?.remoteId) + if (mWaitingToPreview != null && + mWaitingToPreview?.isDown == true && + EditImageActivity.OPEN_IMAGE_EDITOR == downloadBehaviour + ) { + mWaitingToPreview?.let { + startImageEditor(it) + } + } + } + } + + fun isDescendant(downloadedRemotePath: String?): Boolean { + val currentDir = getCurrentDir() + return currentDir != null && + downloadedRemotePath != null && + downloadedRemotePath.startsWith(currentDir.remotePath) + } + + fun isAscendant(linkedToRemotePath: String): Boolean { + val currentDir = getCurrentDir() + return currentDir != null && currentDir.remotePath.startsWith(linkedToRemotePath) + } + + fun isSameAccount(intent: Intent): Boolean { + val accountName = intent.getStringExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME) + return accountName != null && account != null && accountName == account.name + } + } + + fun browseToRoot() { + val listOfFiles = this.listOfFilesFragment + if (listOfFiles != null) { // should never be null, indeed + val root = storageManager.getFileByPath(OCFile.ROOT_PATH) + listOfFiles.listDirectory(root, MainApp.isOnlyOnDevice(), false) + file = listOfFiles.currentFile + startSyncFolderOperation(root, false) + } + binding.fabMain.setImageResource(R.drawable.ic_plus) + resetTitleBarAndScrolling() + } + + override fun onBrowsedDownTo(directory: OCFile?) { + file = directory + resetTitleBarAndScrolling() + startSyncFolderOperation(directory, false) + startMetadataSyncForCurrentDir() + } + + /** + * Shows the information of the [OCFile] received as a parameter. + * + * @param file [OCFile] whose details will be shown + */ + override fun showDetails(file: OCFile?) { + showDetails(file, 0) + } + + /** + * Shows the information of the [OCFile] received as a parameter. + * + * @param file [OCFile] whose details will be shown + * @param activeTab the active tab in the details view + */ + override fun showDetails(file: OCFile?, activeTab: Int) { + val currentUser = user.orElseThrow(Supplier { RuntimeException() }) + + resetScrolling(true) + + val detailFragment: Fragment = FileDetailFragment.newInstance(file, currentUser, activeTab) + setLeftFragment(detailFragment, false) + configureToolbarForPreview(file) + } + + /** + * Prevents content scrolling and toolbar collapse + */ + @VisibleForTesting + fun lockScrolling() { + binding.appbar.appbar.setExpanded(true, false) + val appbarParams = binding.appbar.toolbarFrame.layoutParams as AppBarLayout.LayoutParams + appbarParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL) + binding.appbar.toolbarFrame.layoutParams = appbarParams + } + + /** + * Resets content scrolling and toolbar collapse + */ + @VisibleForTesting + fun resetScrolling(expandAppBar: Boolean) { + val appbarParams = binding.appbar.toolbarFrame.layoutParams as AppBarLayout.LayoutParams + appbarParams.setScrollFlags( + AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS + ) + binding.appbar.toolbarFrame.layoutParams = appbarParams + if (expandAppBar) { + binding.appbar.appbar.setExpanded(true, false) + } + } + + public override fun updateActionBarTitleAndHomeButton(chosenFile: OCFile?) { + var chosenFile = chosenFile + if (chosenFile == null) { + chosenFile = file // if no file is passed, current file decides + } + super.updateActionBarTitleAndHomeButton(chosenFile) + } + + override fun isDrawerIndicatorAvailable(): Boolean = isRoot(getCurrentDir()) + + private fun observeWorkerState() { + WorkerStateLiveData.Companion.instance().observe( + this, + Observer { state: WorkerState? -> + when (state) { + is DownloadStarted -> { + Log_OC.d(TAG, "Download worker started") + handleDownloadWorkerState() + } + + is DownloadFinished -> { + fileDownloadProgressListener = null + previewFile(state) + } + + is UploadFinished -> { + refreshList() + } + + is OfflineOperationsCompleted -> { + refreshCurrentDirectory() + } + + else -> { + } + } + } + ) + } + + private fun previewFile(finishedState: DownloadFinished) { + if (fileIDForImmediatePreview == -1L) { + return + } + + val currentFile = finishedState.currentFile + if (currentFile == null) { + return + } + + if (fileIDForImmediatePreview != currentFile.fileId || !currentFile.isDown) { + return + } + + fileIDForImmediatePreview = -1 + if (PreviewImageFragment.canBePreviewed(currentFile)) { + startImagePreview(currentFile, currentFile.isDown) + } else { + previewFile(currentFile, null) + } + } + + fun previewImageWithSearchContext(file: OCFile, searchFragment: Boolean, currentSearchType: SearchType?) { + // preview image - it handles the download, if needed + if (searchFragment) { + val type = when (currentSearchType) { + SearchType.FAVORITE_SEARCH -> VirtualFolderType.FAVORITE + SearchType.GALLERY_SEARCH -> VirtualFolderType.GALLERY + else -> VirtualFolderType.NONE + } + + startImagePreview(file, type, file.isDown) + } else { + startImagePreview(file, file.isDown) + } + } + + fun previewFile(file: OCFile, setFabVisible: CompletionCallback?) { + if (!file.isDown) { + Log_OC.d(TAG, "File is not downloaded, cannot be previewed") + return + } + + if (MimeTypeUtil.isVCard(file)) { + startContactListFragment(file) + } else if (MimeTypeUtil.isPDF(file)) { + startPdfPreview(file) + } else if (PreviewTextFileFragment.canBePreviewed(file)) { + setFabVisible?.onComplete(false) + startTextPreview(file, false) + } else if (PreviewMediaActivity.Companion.canBePreviewed(file)) { + setFabVisible?.onComplete(false) + startMediaPreview(file, 0, true, true, false, true) + } else { + fileOperationsHelper.openFile(file) + } + } + + fun refreshCurrentDirectory() { + val currentDir = + if (getCurrentDir() != + null + ) { + storageManager.getFileByDecryptedRemotePath(getCurrentDir().remotePath) + } else { + null + } + + val lastFragment = lastFragment() + + var fileListFragment: OCFileListFragment? = null + if (lastFragment is OCFileListFragment) { + fileListFragment = lastFragment + } + if (fileListFragment == null) { + fileListFragment = listOfFilesFragment + } + fileListFragment?.listDirectory(currentDir, MainApp.isOnlyOnDevice(), false) + } + + private fun handleDownloadWorkerState() { + if (mWaitingToPreview != null && storageManager != null) { + mWaitingToPreview = mWaitingToPreview?.fileId?.let { storageManager.getFileById(it) } + if (mWaitingToPreview != null && mWaitingToPreview?.isDown == false) { + requestForDownload() + } + } + } + + override fun newTransferenceServiceConnection(): ServiceConnection = ListServiceConnection() + + /** + * Defines callbacks for service binding, passed to bindService() + * TODO: Check if this can be removed since download and uploads uses work manager now. + */ + private inner class ListServiceConnection : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) = Unit + + override fun onServiceDisconnected(component: ComponentName) { + if (component == ComponentName(this@FileDisplayActivity, FileDownloadWorker::class.java)) { + Log_OC.d(TAG, "Download service disconnected") + fileDownloadProgressListener = null + } + } + } + + /** + * Updates the view associated to the activity after the finish of some operation over files in the current + * account. + * + * @param operation Removal operation performed. + * @param result Result of the removal. + */ + override fun onRemoteOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + super.onRemoteOperationFinish(operation, result) + + when (operation) { + is RemoveFileOperation -> { + onRemoveFileOperationFinish(operation, result) + } + + is RenameFileOperation -> { + onRenameFileOperationFinish(operation, result) + } + + is SynchronizeFileOperation -> { + onSynchronizeFileOperationFinish(operation, result) + } + + is CreateFolderOperation -> { + onCreateFolderOperationFinish(operation, result) + } + + is MoveFileOperation -> { + onMoveFileOperationFinish(operation, result) + } + + is CopyFileOperation -> { + onCopyFileOperationFinish(operation, result) + } + + is RestoreFileVersionRemoteOperation -> { + onRestoreFileVersionOperationFinish(result) + } + } + } + + private val fileListFragment: OCFileListFragment? + get() = if (lastFragment() is OCFileListFragment) lastFragment() as OCFileListFragment else listOfFilesFragment + + private fun refreshGalleryFragmentIfNeeded() { + val fileListFragment = this.fileListFragment + if (fileListFragment is GalleryFragment) { + startPhotoSearch(R.id.nav_gallery) + } + } + + private fun refreshShowDetails() { + val details = this.leftFragment + if (details is FileFragment) { + var file = details.file + if (file != null) { + file = storageManager.getFileByPath(file.remotePath) + if (details is PreviewTextFragment) { + // Refresh OCFile of the fragment + (details as PreviewTextFileFragment).updateFile(file) + } else { + showDetails(file) + } + } + supportInvalidateOptionsMenu() + } + } + + /** + * Updates the view associated to the activity after the finish of an operation trying to remove a file. + * + * @param operation Removal operation performed. + * @param result Result of the removal. + */ + private fun onRemoveFileOperationFinish(operation: RemoveFileOperation, result: RemoteOperationResult<*>) { + if (!operation.isInBackground) { + DisplayUtils.showSnackMessage( + this, + ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()) + ) + } + + if (result.isSuccess) { + val removedFile = operation.file + tryStopPlaying(removedFile) + val leftFragment = this.leftFragment + + // check if file is still available, if so do nothing + val fileAvailable = storageManager.fileExists(removedFile.fileId) + if (leftFragment is FileFragment && !fileAvailable && removedFile == leftFragment.file) { + file = storageManager.getFileById(removedFile.parentId) + resetTitleBarAndScrolling() + } + val parentFile = storageManager.getFileById(removedFile.parentId) + if (parentFile != null && parentFile == getCurrentDir()) { + updateListOfFilesFragment(false) + } else if (this.leftFragment is GalleryFragment) { + val galleryFragment = leftFragment as GalleryFragment + galleryFragment.onRefresh() + } else if (leftFragment is OCFileListFragment && + SearchRemoteOperation.SearchType.FAVORITE_SEARCH == leftFragment.searchEvent?.searchType + ) { + leftFragment.adapter?.run { + val file = files.find { it.fileId == removedFile.fileId } + if (file != null) { + val pos = getItemPosition(file) + files.remove(file) + notifyItemRemoved(pos) + } + } + } + supportInvalidateOptionsMenu() + refreshGalleryFragmentIfNeeded() + fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir) + } else { + if (result.isSslRecoverableException) { + mLastSslUntrustedServerResult = result + showUntrustedCertDialog(mLastSslUntrustedServerResult) + } + } + } + + private fun onRestoreFileVersionOperationFinish(result: RemoteOperationResult<*>) { + if (result.isSuccess) { + val file = getFile() + + // delete old local copy + if (file.isDown) { + val list: MutableList = ArrayList() + list.add(file) + fileOperationsHelper.removeFiles(list, true, true) + + // download new version, only if file was previously download + showSyncLoadingDialog(file.isFolder) + fileOperationsHelper.syncFile(file) + } + + val parent = storageManager.getFileById(file.parentId) + startSyncFolderOperation(parent, ignoreETag = true, ignoreFocus = true) + + val leftFragment = this.leftFragment + if (leftFragment is FileDetailFragment) { + leftFragment.getFileDetailActivitiesFragment().reload() + } + + DisplayUtils.showSnackMessage(this, R.string.file_version_restored_successfully) + } else { + DisplayUtils.showSnackMessage(this, R.string.file_version_restored_error) + } + } + + private fun tryStopPlaying(file: OCFile) { + // placeholder for stop-on-delete future code + if (mPlayerConnection != null && MimeTypeUtil.isAudio(file) && mPlayerConnection?.isPlaying() == true) { + mPlayerConnection?.stop(file) + } + } + + /** + * Updates the view associated to the activity after the finish of an operation trying to move a file. + * + * @param operation Move operation performed. + * @param result Result of the move operation. + */ + private fun onMoveFileOperationFinish(operation: MoveFileOperation?, result: RemoteOperationResult<*>) { + if (!result.isSuccess) { + try { + DisplayUtils.showSnackMessage( + this, + ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()) + ) + } catch (e: Resources.NotFoundException) { + Log_OC.e(TAG, "Error while trying to show fail message ", e) + } + } + } + + /** + * Updates the view associated to the activity after the finish of an operation trying to copy a file. + * + * @param operation Copy operation performed. + * @param result Result of the copy operation. + */ + private fun onCopyFileOperationFinish(operation: CopyFileOperation?, result: RemoteOperationResult<*>) { + if (result.isSuccess) { + updateListOfFilesFragment(false) + refreshGalleryFragmentIfNeeded() + } else { + try { + DisplayUtils.showSnackMessage( + this, + ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()) + ) + } catch (e: Resources.NotFoundException) { + Log_OC.e(TAG, "Error while trying to show fail message ", e) + } + } + } + + /** + * Updates the view associated to the activity after the finish of an operation trying to rename a file. + * + * @param operation Renaming operation performed. + * @param result Result of the renaming. + */ + private fun onRenameFileOperationFinish(operation: RenameFileOperation, result: RemoteOperationResult<*>) { + val optionalUser = user + val renamedFile = operation.file + if (result.isSuccess && optionalUser.isPresent) { + val currentUser = optionalUser.get() + val leftFragment = this.leftFragment + if (leftFragment is FileFragment) { + if (leftFragment is FileDetailFragment && renamedFile == leftFragment.file) { + leftFragment.updateFileDetails(renamedFile, currentUser) + showDetails(renamedFile) + } else if (leftFragment is PreviewMediaFragment && renamedFile == leftFragment.file) { + leftFragment.updateFile(renamedFile) + if (PreviewMediaFragment.canBePreviewed(renamedFile)) { + val position = leftFragment.position + startMediaPreview(renamedFile, position, true, true, true, false) + } else { + fileOperationsHelper.openFile(renamedFile) + } + } else if (leftFragment is PreviewTextFragment && renamedFile == leftFragment.file) { + (leftFragment as PreviewTextFileFragment).updateFile(renamedFile) + if (PreviewTextFileFragment.canBePreviewed(renamedFile)) { + startTextPreview(renamedFile, true) + } else { + fileOperationsHelper.openFile(renamedFile) + } + } + } + + val file = storageManager.getFileById(renamedFile.parentId) + if (file != null && file == getCurrentDir()) { + updateListOfFilesFragment(false) + } + refreshGalleryFragmentIfNeeded() + fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir) + } else { + DisplayUtils.showSnackMessage( + this, + ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()) + ) + + if (result.isSslRecoverableException) { + mLastSslUntrustedServerResult = result + showUntrustedCertDialog(mLastSslUntrustedServerResult) + } + } + } + + private fun onSynchronizeFileOperationFinish( + operation: SynchronizeFileOperation, + result: RemoteOperationResult<*> + ) { + if (result.isSuccess && operation.transferWasRequested()) { + val syncedFile = operation.localFile + onTransferStateChanged(syncedFile, true, true) + supportInvalidateOptionsMenu() + refreshShowDetails() + } + } + + /** + * Updates the view associated to the activity after the finish of an operation trying create a new folder + * + * @param operation Creation operation performed. + * @param result Result of the creation. + */ + private fun onCreateFolderOperationFinish(operation: CreateFolderOperation, result: RemoteOperationResult<*>) { + if (result.isSuccess) { + val fileListFragment = this.listOfFilesFragment + fileListFragment?.onItemClicked(storageManager.getFileByDecryptedRemotePath(operation.getRemotePath())) + } else { + try { + if (RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS == result.code) { + DisplayUtils.showSnackMessage(this, R.string.folder_already_exists) + } else { + DisplayUtils.showSnackMessage( + this, + ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()) + ) + } + } catch (e: Resources.NotFoundException) { + Log_OC.e(TAG, "Error while trying to show fail message ", e) + } + } + } + + /** + * {@inheritDoc} + */ + override fun onTransferStateChanged(file: OCFile, downloading: Boolean, uploading: Boolean) { + updateListOfFilesFragment(false) + val leftFragment = this.leftFragment + val optionalUser = user + if (leftFragment is FileDetailFragment && file == leftFragment.file && optionalUser.isPresent) { + val currentUser = optionalUser.get() + if (downloading || uploading) { + leftFragment.updateFileDetails(file, currentUser) + } else { + if (!file.fileExists()) { + resetTitleBarAndScrolling() + } else { + leftFragment.updateFileDetails(false, true) + } + } + } + } + + private fun requestForDownload() { + val user = user.orElseThrow(Supplier { RuntimeException() }) + mWaitingToPreview?.let { + FileDownloadHelper.Companion.instance().downloadFileIfNotStartedBefore(user, it) + } + } + + override fun onSavedCertificate() { + startSyncFolderOperation(getCurrentDir(), false) + } + + /** + * Starts an operation to refresh the requested folder. + * + * + * The operation is run in a new background thread created on the fly. + * + * + * The refresh updates is a "light sync": properties of regular files in folder are updated (including associated + * shares), but not their contents. Only the contents of files marked to be kept-in-sync are synchronized too. + * + * @param folder Folder to refresh. + * @param ignoreETag If 'true', the data from the server will be fetched and sync'ed even if the eTag didn't + * change. + * @param ignoreFocus reloads file list even without focus, e.g. on tablet mode, focus can still be in detail view + */ + /** + * Starts an operation to refresh the requested folder. + * + * + * The operation is run in a new background thread created on the fly. + * + * + * The refresh updates is a "light sync": properties of regular files in folder are updated (including associated + * shares), but not their contents. Only the contents of files marked to be kept-in-sync are synchronized too. + * + * @param folder Folder to refresh. + * @param ignoreETag If 'true', the data from the server will be fetched and sync'ed even if the eTag didn't + * change. + */ + @JvmOverloads + fun startSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean, ignoreFocus: Boolean = false) { + Log_OC.d(TAG, "startSyncFolderOperation called, ignoreEtag: $ignoreETag, ignoreFocus: $ignoreFocus") + + // the execution is slightly delayed to allow the activity get the window focus if it's being started + // or if the method is called from a dialog that is being dismissed + + if (TextUtils.isEmpty(searchQuery) && user.isPresent) { + handler.postDelayed({ + val user = getUser() + if (!ignoreFocus && !hasWindowFocus() || !user.isPresent) { + // do not refresh if the user rotates the device while another window has focus + // or if the current user is no longer valid + return@postDelayed + } + + val currentSyncTime = System.currentTimeMillis() + mSyncInProgress = true + + // perform folder synchronization + val refreshFolderOperation: RemoteOperation<*> = RefreshFolderOperation( + folder, + currentSyncTime, + false, + ignoreETag, + storageManager, + user.get(), + applicationContext + ) + refreshFolderOperation.execute( + account, + MainApp.getAppContext(), + this@FileDisplayActivity, + null, + null + ) + + fetchRecommendedFilesIfNeeded(ignoreETag, folder) + mSyncInProgress = false + ocFileListFragment?.setLoading(false) + }, DELAY_TO_REQUEST_REFRESH_OPERATION_LATER) + } + } + + private fun fetchRecommendedFilesIfNeeded(ignoreETag: Boolean, folder: OCFile?) { + if (folder?.isRootDirectory == false || capabilities == null || capabilities.recommendations.isFalse) { + return + } + + if (user.isPresent) { + val accountName = user.get().accountName + val fragment = this.listOfFilesFragment + lifecycleScope.launch(Dispatchers.IO) { + val recommendedFiles = filesRepository.fetchRecommendedFiles(accountName, ignoreETag, storageManager) + withContext(Dispatchers.Main) { + fragment?.adapter?.updateRecommendedFiles(recommendedFiles) + } + } + } + } + + private fun requestForDownload(file: OCFile, downloadBehaviour: String, packageName: String, activityName: String) { + val currentUser = user.orElseThrow(Supplier { RuntimeException() }) + if (!FileDownloadHelper.Companion.instance().isDownloading(currentUser, file)) { + FileDownloadHelper.Companion.instance().downloadFile( + currentUser, + file, + downloadBehaviour, + DownloadType.DOWNLOAD, + activityName, + packageName, + null + ) + } + } + + private fun sendDownloadedFile(packageName: String, activityName: String) { + val waitingToSend = mWaitingToSend + if (waitingToSend != null) { + val sendIntent = IntentUtil.createSendIntent(this, waitingToSend) + sendIntent.component = ComponentName(packageName, activityName) + + // Show dialog + val sendTitle = getString(R.string.activity_chooser_send_file_title) + startActivity(Intent.createChooser(sendIntent, sendTitle)) + } else { + Log_OC.e(TAG, "Trying to send a NULL OCFile") + } + + mWaitingToSend = null + } + + /** + * Requests the download of the received [OCFile] , updates the UI to monitor the download progress and + * prepares the activity to send the file when the download finishes. + * + * @param file [OCFile] to download and preview. + * @param packageName + * @param activityName + */ + fun startDownloadForSending(file: OCFile?, downloadBehaviour: String, packageName: String, activityName: String) { + mWaitingToSend = file + mWaitingToSend?.let { + requestForDownload(it, downloadBehaviour, packageName, activityName) + } + } + + fun startImagePreview(file: OCFile, showPreview: Boolean) { + val showDetailsIntent = Intent(this, PreviewImageActivity::class.java) + showDetailsIntent.putExtra(EXTRA_FILE, file) + showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) + showDetailsIntent.putExtra( + EXTRA_USER, + user.orElseThrow(Supplier { RuntimeException() }) + ) + if (showPreview) { + startActivity(showDetailsIntent) + } else { + val fileOperationsHelper = + FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) + fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent) + } + } + + fun startImagePreview(file: OCFile, type: VirtualFolderType?, showPreview: Boolean) { + val showDetailsIntent = Intent(this, PreviewImageActivity::class.java) + showDetailsIntent.putExtra(EXTRA_FILE, file) + showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) + showDetailsIntent.putExtra( + EXTRA_USER, + user.orElseThrow(Supplier { RuntimeException() }) + ) + showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type) + + if (showPreview) { + startActivity(showDetailsIntent) + } else { + val fileOperationsHelper = FileOperationsHelper( + this, + userAccountManager, + connectivityService, + editorUtils + ) + fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent) + } + } + + /** + * Stars the preview of an already down media [OCFile]. + * + * @param file Media [OCFile] to preview. + * @param startPlaybackPosition Media position where the playback will be started, in milliseconds. + * @param autoplay When 'true', the playback will start without user interactions. + */ + fun startMediaPreview( + file: OCFile, + startPlaybackPosition: Long, + autoplay: Boolean, + showPreview: Boolean, + streamMedia: Boolean, + showInActivity: Boolean + ) { + val user = getUser() + if (!user.isPresent) { + return // not reachable under normal conditions + } + val actualUser = user.get() + if (showPreview && file.isDown && !file.isDownloading || streamMedia) { + if (showInActivity) { + startMediaActivity(file, startPlaybackPosition, autoplay, actualUser) + } else { + configureToolbarForPreview(file) + val mediaFragment: Fragment = newInstance(file, user.get(), startPlaybackPosition, autoplay, false) + setLeftFragment(mediaFragment, false) + } + } else { + val previewIntent = Intent() + previewIntent.putExtra(EXTRA_FILE, file) + previewIntent.putExtra(PreviewMediaFragment.EXTRA_START_POSITION, startPlaybackPosition) + previewIntent.putExtra(PreviewMediaFragment.EXTRA_AUTOPLAY, autoplay) + val fileOperationsHelper = + FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) + fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent) + } + } + + private fun startMediaActivity(file: OCFile?, startPlaybackPosition: Long, autoplay: Boolean, user: User?) { + val previewMediaIntent = Intent(this, PreviewMediaActivity::class.java) + previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_FILE, file) + + // Safely handle the absence of a user + if (user != null) { + previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_USER, user) + } + + previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_START_POSITION, startPlaybackPosition) + previewMediaIntent.putExtra(PreviewMediaActivity.EXTRA_AUTOPLAY, autoplay) + startActivity(previewMediaIntent) + } + + fun configureToolbarForPreview(file: OCFile?) { + lockScrolling() + super.updateActionBarTitleAndHomeButton(file) + } + + /** + * Starts the preview of a text file [OCFile]. + * + * @param file Text [OCFile] to preview. + */ + fun startTextPreview(file: OCFile?, showPreview: Boolean) { + val optUser = user + if (!optUser.isPresent) { + // remnants of old unsafe system; do not crash, silently stop + return + } + val user = optUser.get() + if (showPreview) { + val fragment = PreviewTextFileFragment.create(user, file, searchOpen, searchQuery) + setLeftFragment(fragment, false) + configureToolbarForPreview(file) + } else { + val previewIntent = Intent() + previewIntent.putExtra(EXTRA_FILE, file) + previewIntent.putExtra(TEXT_PREVIEW, true) + val fileOperationsHelper = + FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) + fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent) + } + } + + /** + * Starts rich workspace preview for a folder. + * + * @param folder [OCFile] to preview its rich workspace. + */ + fun startRichWorkspacePreview(folder: OCFile?) { + val args = Bundle() + args.putParcelable(EXTRA_FILE, folder) + configureToolbarForPreview(folder) + val textPreviewFragment = + Fragment.instantiate(applicationContext, PreviewTextStringFragment::class.java.name, args) + setLeftFragment(textPreviewFragment, false) + } + + fun startContactListFragment(file: OCFile?) { + val user = user.orElseThrow(Supplier { RuntimeException() }) + ContactsPreferenceActivity.startActivityWithContactsFile(this, user, file) + } + + fun startPdfPreview(file: OCFile) { + if (fileOperationsHelper.canOpenFile(file)) { + // prefer third party PDF apps + fileOperationsHelper.openFile(file) + } else { + val pdfFragment: Fragment = newInstance(file) + + setLeftFragment(pdfFragment, false) + configureToolbarForPreview(file) + setMainFabVisible(false) + } + } + + /** + * Requests the download of the received [OCFile] , updates the UI to monitor the download progress and + * prepares the activity to preview or open the file when the download finishes. + * + * @param file [OCFile] to download and preview. + * @param parentFolder [OCFile] containing above file + */ + fun startDownloadForPreview(file: OCFile, parentFolder: OCFile?) { + if (!file.isFileEligibleForImmediatePreview) { + val currentUser = user + if (currentUser.isPresent) { + val detailFragment: Fragment = FileDetailFragment.newInstance(file, parentFolder, currentUser.get()) + setLeftFragment(detailFragment, false) + } + } + + configureToolbarForPreview(file) + mWaitingToPreview = file + requestForDownload() + setFile(file) + } + + /** + * Opens EditImageActivity with given file loaded. If file is not available locally, it will be synced before + * opening the image editor. + * + * @param file [OCFile] (image) to be loaded into image editor + */ + fun startImageEditor(file: OCFile) { + if (file.isDown) { + val editImageIntent = Intent(this, EditImageActivity::class.java) + editImageIntent.putExtra(EditImageActivity.EXTRA_FILE, file) + startActivity(editImageIntent) + } else { + mWaitingToPreview = file + requestForDownload( + file, + EditImageActivity.OPEN_IMAGE_EDITOR, + packageName, + this.javaClass.simpleName + ) + updateActionBarTitleAndHomeButton(file) + setFile(file) + } + } + + /** + * Request stopping the upload/download operation in progress over the given [OCFile] file. + * + * @param file [OCFile] file which operation are wanted to be cancel + */ + fun cancelTransference(file: OCFile) { + fileOperationsHelper.cancelTransference(file) + if (mWaitingToPreview != null && mWaitingToPreview?.remotePath == file.remotePath) { + mWaitingToPreview = null + } + if (mWaitingToSend != null && mWaitingToSend?.remotePath == file.remotePath) { + mWaitingToSend = null + } + onTransferStateChanged(file, false, false) + } + + /** + * Request stopping all upload/download operations in progress over the given [OCFile] files. + * + * @param files collection of [OCFile] files which operations are wanted to be cancel + */ + fun cancelTransference(files: MutableCollection) { + for (file in files) { + cancelTransference(file) + } + } + + override fun onRefresh(ignoreETag: Boolean) { + syncAndUpdateFolder(ignoreETag, ignoreFocus = false) + } + + override fun onRefresh() { + syncAndUpdateFolder(ignoreETag = true, ignoreFocus = false) + } + + private fun syncAndUpdateFolder(ignoreETag: Boolean, ignoreFocus: Boolean) { + val listOfFiles = this.listOfFilesFragment + if (listOfFiles == null || listOfFiles.isSearchFragment) { + return + } + + val folder = listOfFiles.currentFile ?: return + startSyncFolderOperation(folder, ignoreETag, ignoreFocus) + } + + override fun showFiles(onDeviceOnly: Boolean, personalFiles: Boolean) { + super.showFiles(onDeviceOnly, personalFiles) + if (onDeviceOnly) { + updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_on_device)) + } + val ocFileListFragment = this.listOfFilesFragment + if (ocFileListFragment != null && + (ocFileListFragment !is GalleryFragment) && + (ocFileListFragment !is SharedListFragment) + ) { + ocFileListFragment.refreshDirectory() + } else { + this.leftFragment = OCFileListFragment() + } + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + fun onMessageEvent(event: SearchEvent) { + if (SearchRemoteOperation.SearchType.PHOTO_SEARCH == event.searchType) { + Log_OC.d(this, "Switch to photo search fragment") + this.leftFragment = GalleryFragment() + } else if (event.searchType == SearchRemoteOperation.SearchType.SHARED_FILTER) { + Log_OC.d(this, "Switch to Shared fragment") + this.leftFragment = SharedListFragment() + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onMessageEvent(event: SyncEventFinished) { + val bundle = event.intent.extras + val file = bundle?.get(EXTRA_FILE) as OCFile? ?: return + + if (event.intent.getBooleanExtra(TEXT_PREVIEW, false)) { + startTextPreview(file, true) + } else if (bundle.containsKey(PreviewMediaFragment.EXTRA_START_POSITION)) { + val startPosition = bundle.get(PreviewMediaFragment.EXTRA_START_POSITION) as Long + val autoPlay = bundle.get(PreviewMediaFragment.EXTRA_AUTOPLAY) as Boolean + startMediaPreview( + file, + startPosition, + autoPlay, + true, + true, + true + ) + } else if (bundle.containsKey(PreviewImageActivity.EXTRA_VIRTUAL_TYPE)) { + val virtualType = bundle.get(PreviewImageActivity.EXTRA_VIRTUAL_TYPE) as VirtualFolderType? + startImagePreview( + file, + virtualType, + true + ) + } else { + startImagePreview(file, true) + } + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + fun onMessageEvent(event: TokenPushEvent?) { + if (!preferences.isKeysReInitEnabled()) { + PushUtils.reinitKeys(userAccountManager) + } else { + PushUtils.pushRegistrationToServer(userAccountManager, preferences.getPushToken()) + } + } + + public override fun onStart() { + super.onStart() + val optionalUser = user + val storageManager = getStorageManager() + if (optionalUser.isPresent && storageManager != null) { + /** Check whether the 'main' OCFile handled by the Activity is contained in the */ + // current Account + var file = getFile() + // get parent from path + if (file != null) { + if (file.isDown && file.lastSyncDateForProperties == 0L) { + // upload in progress - right now, files are not inserted in the local + // cache until the upload is successful get parent from path + val parentPath = + file.remotePath.substring(0, file.remotePath.lastIndexOf(file.fileName)) + if (storageManager.getFileByPath(parentPath) == null) { + file = null // not able to know the directory where the file is uploading + } + } else { + file = storageManager.getFileByPath(file.remotePath) + // currentDir = null if not in the current Account + } + } + if (file == null) { + // fall back to root folder + file = storageManager.getFileByPath(OCFile.ROOT_PATH) // never returns null + } + setFile(file) + + val user = optionalUser.get() + setupDrawer() + + mSwitchAccountButton.tag = user.accountName + DisplayUtils.setAvatar( + user, + this, + getResources().getDimension(R.dimen.nav_drawer_menu_avatar_radius), + getResources(), + mSwitchAccountButton, + this + ) + val userChanged = (user.accountName != lastDisplayedAccountName) + if (userChanged) { + Log_OC.d(TAG, "Initializing Fragments in onAccountChanged..") + initFragments() + if (file.isFolder && TextUtils.isEmpty(searchQuery)) { + startSyncFolderOperation(file, false) + } + } else { + updateActionBarTitleAndHomeButton(if (file.isFolder) null else file) + } + } + + val newLastDisplayedAccountName = optionalUser.orElse(null).accountName + preferences.lastDisplayedAccountName = newLastDisplayedAccountName + lastDisplayedAccountName = newLastDisplayedAccountName + + EventBus.getDefault().post(TokenPushEvent()) + checkForNewDevVersionNecessary(applicationContext) + } + + override fun onRestart() { + super.onRestart() + checkForNewDevVersionNecessary(applicationContext) + } + + fun setSearchQuery(query: String?) { + searchQuery = query + } + + private fun handleOpenFileViaIntent(intent: Intent) { + DisplayUtils.showSnackMessage(this, getString(R.string.retrieving_file)) + + val userName = intent.getStringExtra(KEY_ACCOUNT) + val fileId = intent.getStringExtra(KEY_FILE_ID) + val filePath = intent.getStringExtra(KEY_FILE_PATH) + + val intentData = intent.data + if (userName == null && fileId == null && intentData != null) { + openDeepLink(intentData) + } else { + val optionalUser = if (userName == null) user else userAccountManager.getUser(userName) + if (optionalUser.isPresent) { + if (!TextUtils.isEmpty(fileId)) { + openFile(optionalUser.get(), fileId) + } else if (!TextUtils.isEmpty(filePath)) { + openFileByPath(optionalUser.get(), filePath) + } else { + accountClicked(optionalUser.get().hashCode()) + } + } else { + DisplayUtils.showSnackMessage(this, getString(R.string.associated_account_not_found)) + } + } + } + + private fun openDeepLink(uri: Uri) { + val linkHandler = DeepLinkHandler(userAccountManager) + val match = linkHandler.parseDeepLink(uri) + + if (match == null) { + handleDeepLink(uri) + } else if (match.users.isEmpty()) { + DisplayUtils.showSnackMessage(this, getString(R.string.associated_account_not_found)) + } else if (match.users.size == SINGLE_USER_SIZE) { + openFile(match.users[0], match.fileId) + } else { + selectUserAndOpenFile(match.users.toMutableList(), match.fileId) + } + } + + private fun selectUserAndOpenFile(users: MutableList, fileId: String?) { + val userNames = arrayOfNulls(users.size) + for (i in userNames.indices) { + userNames[i] = users[i]?.accountName + } + val builder = MaterialAlertDialogBuilder(this) + builder.setTitle(R.string.common_choose_account) + .setItems(userNames) { dialog: DialogInterface?, which: Int -> + val user = users[which] + openFile(user, fileId) + showLoadingDialog(getString(R.string.retrieving_file)) + } + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(applicationContext, builder) + + val dialog = builder.create() + dismissLoadingDialog() + dialog.show() + } + + private fun openFile(user: User?, fileId: String?) { + setUser(user) + + if (fileId == null) { + onFileRequestError(null) + return + } + + var storageManager = getStorageManager() + + if (storageManager == null) { + storageManager = FileDataStorageManager(user, contentResolver) + } + + val fetchRemoteFileTask = FetchRemoteFileTask(user, fileId, storageManager, this) + fetchRemoteFileTask.execute() + } + + private fun openFileByPath(user: User, filepath: String?) { + setUser(user) + + if (filepath == null) { + onFileRequestError(null) + return + } + + var storageManager = getStorageManager() + + if (storageManager == null) { + storageManager = FileDataStorageManager(user, contentResolver) + } + + val client: OwnCloudClient + try { + client = clientFactory.create(user) + } catch (_: CreationException) { + onFileRequestError(null) + return + } + + val getRemoteFileTask = GetRemoteFileTask(this, filepath, client, storageManager, user) + asyncRunner.postQuickTask( + getRemoteFileTask, + { result: GetRemoteFileTask.Result -> this.onFileRequestResult(result) }, + { throwable: Throwable? -> this.onFileRequestError(throwable) } + ) + } + + private fun onFileRequestError(throwable: Throwable?) { + dismissLoadingDialog() + DisplayUtils.showSnackMessage(this, getString(R.string.error_retrieving_file)) + Log_OC.e(TAG, "Requesting file from remote failed!", throwable) + } + + private fun onFileRequestResult(result: GetRemoteFileTask.Result) { + dismissLoadingDialog() + + file = result.file + + val fileFragment = OCFileListFragment() + this.leftFragment = fileFragment + + supportFragmentManager.executePendingTransactions() + + fileFragment.onItemClicked(result.file) + } + + fun performUnifiedSearch(query: String, listOfHiddenFiles: ArrayList?) { + val unifiedSearchFragment = UnifiedSearchFragment.Companion.newInstance(query, listOfHiddenFiles) + setLeftFragment(unifiedSearchFragment, false) + } + + fun setMainFabVisible(visible: Boolean) { + val visibility = if (visible) View.VISIBLE else View.GONE + binding.fabMain.visibility = visibility + } + + fun showFile(selectedFile: OCFile?, message: String?) { + dismissLoadingDialog() + + getOCFileListFragmentFromFile(object : TransactionInterface { + override fun onOCFileListFragmentComplete(listOfFiles: OCFileListFragment) { + if (TextUtils.isEmpty(message)) { + val temp = file + file = getCurrentDir() + listOfFiles.listDirectory(getCurrentDir(), temp, MainApp.isOnlyOnDevice(), false) + updateActionBarTitleAndHomeButton(null) + } else { + val view = listOfFiles.view + if (view != null) { + DisplayUtils.showSnackMessage(view, message) + } + } + if (selectedFile != null) { + listOfFiles.onItemClicked(selectedFile) + } + } + }) + } + + // region MetadataSyncJob + private fun startMetadataSyncForRoot() { + backgroundJobManager.startMetadataSyncJob(OCFile.ROOT_PATH) + } + + private fun startMetadataSyncForCurrentDir() { + val currentDirId = file?.decryptedRemotePath ?: return + backgroundJobManager.startMetadataSyncJob(currentDirId) + } + // endregion + + companion object { + const val RESTART: String = "RESTART" + const val ALL_FILES: String = "ALL_FILES" + const val LIST_GROUPFOLDERS: String = "LIST_GROUPFOLDERS" + const val SINGLE_USER_SIZE: Int = 1 + const val OPEN_FILE: String = "NC_OPEN_FILE" + + const val TAG_PUBLIC_LINK: String = "PUBLIC_LINK" + const val FTAG_CHOOSER_DIALOG: String = "CHOOSER_DIALOG" + const val KEY_FILE_ID: String = "KEY_FILE_ID" + const val KEY_FILE_PATH: String = "KEY_FILE_PATH" + const val KEY_ACCOUNT: String = "KEY_ACCOUNT" + const val KEY_IS_SORT_GROUP_VISIBLE: String = "KEY_IS_SORT_GROUP_VISIBLE" + + private const val KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW" + private const val KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS" + private const val KEY_WAITING_TO_SEND = "WAITING_TO_SEND" + private const val DIALOG_TAG_SHOW_TOS = "DIALOG_TAG_SHOW_TOS" + + private const val ON_RESUMED_RESET_DELAY = 10000L + + const val ACTION_DETAILS: String = "com.owncloud.android.ui.activity.action.DETAILS" + + @JvmField + val REQUEST_CODE__SELECT_CONTENT_FROM_APPS: Int = REQUEST_CODE__LAST_SHARED + 1 + + @JvmField + val REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM: Int = REQUEST_CODE__LAST_SHARED + 2 + + @JvmField + val REQUEST_CODE__MOVE_OR_COPY_FILES: Int = REQUEST_CODE__LAST_SHARED + 3 + + @JvmField + val REQUEST_CODE__UPLOAD_FROM_CAMERA: Int = REQUEST_CODE__LAST_SHARED + 5 + + @JvmField + val REQUEST_CODE__UPLOAD_FROM_VIDEO_CAMERA: Int = REQUEST_CODE__LAST_SHARED + 6 + + protected val DELAY_TO_REQUEST_REFRESH_OPERATION_LATER: Long = DELAY_TO_REQUEST_OPERATIONS_LATER + 350 + + private val TAG: String = FileDisplayActivity::class.java.getSimpleName() + + const val TAG_LIST_OF_FILES: String = "LIST_OF_FILES" + + const val TEXT_PREVIEW: String = "TEXT_PREVIEW" + + const val KEY_IS_SEARCH_OPEN: String = "IS_SEARCH_OPEN" + const val KEY_SEARCH_QUERY: String = "SEARCH_QUERY" + + @JvmStatic + fun openFileIntent(context: Context?, user: User?, file: OCFile?): Intent { + val intent = Intent(context, PreviewImageActivity::class.java) + intent.putExtra(EXTRA_FILE, file) + intent.putExtra(EXTRA_USER, user) + return intent + } + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt index 1814412..050331b 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index 0961de2..89721b3 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 TSI-mc * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity @@ -21,8 +21,11 @@ import android.view.Menu import android.view.MenuItem import android.view.View import androidx.activity.OnBackPressedCallback +import androidx.lifecycle.lifecycleScope import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable +import com.nextcloud.utils.fileNameValidator.FileNameValidator import com.owncloud.android.R import com.owncloud.android.databinding.FilesFolderPickerBinding import com.owncloud.android.databinding.FilesPickerBinding @@ -39,6 +42,7 @@ import com.owncloud.android.syncadapter.FileSyncAdapter import com.owncloud.android.ui.dialog.CreateFolderDialogFragment import com.owncloud.android.ui.dialog.SortingOrderDialogFragment.OnSortingOrderListener import com.owncloud.android.ui.events.SearchEvent +import com.owncloud.android.ui.fragment.EmptyListState import com.owncloud.android.ui.fragment.FileFragment import com.owncloud.android.ui.fragment.OCFileListFragment import com.owncloud.android.utils.DataHolderUtil @@ -46,6 +50,8 @@ import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.ErrorMessageAdapter import com.owncloud.android.utils.FileSortOrder import com.owncloud.android.utils.PathUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.io.File import javax.inject.Inject @@ -58,7 +64,6 @@ open class FolderPickerActivity : OnSortingOrderListener { private var mSyncBroadcastReceiver: SyncBroadcastReceiver? = null - private var mSyncInProgress = false private var mSearchOnlyFolders = false var isDoNotEnterEncryptedFolder = false private set @@ -82,6 +87,8 @@ open class FolderPickerActivity : folderPickerBinding = FilesFolderPickerBinding.inflate(layoutInflater) setContentView(folderPickerBinding.root) } + + OCFileListFragment.isMultipleFileSelectedForCopyOrMove = true } override fun onCreate(savedInstanceState: Bundle?) { @@ -101,10 +108,14 @@ open class FolderPickerActivity : } updateActionBarTitleAndHomeButtonByString(captionText) - setBackgroundText() handleOnBackPressed() } + override fun onDestroy() { + OCFileListFragment.isMultipleFileSelectedForCopyOrMove = false + super.onDestroy() + } + private fun setupActionBar() { findViewById(R.id.sort_list_button_group).visibility = View.VISIBLE @@ -202,30 +213,6 @@ open class FolderPickerActivity : transaction.commit() } - /** - * Show a text message on screen view for notifying user if content is loading or folder is empty - */ - private fun setBackgroundText() { - val listFragment = listOfFilesFragment - - if (listFragment == null) { - Log_OC.e(TAG, "OCFileListFragment is null") - } - - listFragment?.let { - if (!mSyncInProgress) { - it.setMessageForEmptyList( - R.string.folder_list_empty_headline, - R.string.file_list_empty_moving, - R.drawable.ic_list_empty_create_folder, - true - ) - } else { - it.setEmptyListLoadingMessage() - } - } - } - protected val listOfFilesFragment: OCFileListFragment? get() { val listOfFiles = supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FOLDERS) @@ -255,30 +242,39 @@ open class FolderPickerActivity : } private fun startSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean) { - val currentSyncTime = System.currentTimeMillis() - mSyncInProgress = true - - RefreshFolderOperation( - folder, - currentSyncTime, - false, - ignoreETag, - storageManager, - user.orElseThrow { RuntimeException("User not set") }, - applicationContext - ).also { - it.execute(account, this, null, null) + val optionalUser = user ?: return + if (optionalUser.isEmpty) { + return } + val user: User = optionalUser.get() + listOfFilesFragment?.setEmptyListMessage(EmptyListState.LOADING) - listOfFilesFragment?.isLoading = true - setBackgroundText() + lifecycleScope.launch(Dispatchers.IO) { + val currentSyncTime = System.currentTimeMillis() + val operation = RefreshFolderOperation( + folder, + currentSyncTime, + false, + ignoreETag, + storageManager, + user, + applicationContext + ) + operation.execute( + account, + this@FolderPickerActivity, + { _, _ -> + listOfFilesFragment?.setEmptyListMessage(EmptyListState.LOCAL_FILE_LIST_EMPTY_FILE) + }, + null + ) + } } override fun onResume() { super.onResume() Log_OC.e(TAG, "onResume() start") - listOfFilesFragment?.isLoading = mSyncInProgress refreshListOfFilesFragment(false) file = listOfFilesFragment?.currentFile updateUiElements() @@ -292,13 +288,11 @@ open class FolderPickerActivity : Log_OC.d(TAG, "onResume() end") } - private fun getSyncIntentFilter(): IntentFilter { - return IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START).apply { - addAction(FileSyncAdapter.EVENT_FULL_SYNC_END) - addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED) - addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED) - addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED) - } + private fun getSyncIntentFilter(): IntentFilter = IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START).apply { + addAction(FileSyncAdapter.EVENT_FULL_SYNC_END) + addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED) + addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED) + addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED) } override fun onPause() { @@ -379,26 +373,47 @@ open class FolderPickerActivity : private fun toggleChooseEnabled() { if (this is FilePickerActivity) { return + } + + val selectedFolderPathTitle = getSelectedFolderPathTitle() + val isFolderPathValid = if (selectedFolderPathTitle != null) { + FileNameValidator.checkFolderPath(selectedFolderPathTitle, capabilities, this) } else { - folderPickerBinding.folderPickerBtnCopy.isEnabled = checkFolderSelectable() - folderPickerBinding.folderPickerBtnMove.isEnabled = checkFolderSelectable() + true + } + + checkButtonStates(isFolderPathValid) + + if (!isFolderPathValid) { + DisplayUtils.showSnackMessage( + this, + R.string.file_name_validator_error_contains_reserved_names_or_invalid_characters + ) + return + } + } + + private fun checkButtonStates(isConditionMet: Boolean) { + folderPickerBinding.run { + folderPickerBtnChoose.isEnabled = isConditionMet + folderPickerBtnCopy.isEnabled = isFolderSelectable(COPY) && isConditionMet + folderPickerBtnMove.isEnabled = isFolderSelectable(MOVE) && isConditionMet } } // for copy and move, disable selecting parent folder of target files - private fun checkFolderSelectable(): Boolean { - return when { - action != MOVE_OR_COPY -> true - targetFilePaths.isNullOrEmpty() -> true - file?.isFolder != true -> true + private fun isFolderSelectable(type: String): Boolean = when { + action != MOVE_OR_COPY -> true + action == MOVE_OR_COPY && type == COPY -> true + targetFilePaths.isNullOrEmpty() -> true + file?.isFolder != true -> true - // all of the target files are already in the selected directory - targetFilePaths?.all { PathUtils.isDirectParent(file.remotePath, it) } == true -> false + // all of the target files are already in the selected directory + targetFilePaths?.all { PathUtils.isDirectParent(file.remotePath, it) } == true -> false - // some of the target files are parents of the selected folder - targetFilePaths?.any { PathUtils.isAncestor(it, file.remotePath) } == true -> false - else -> true - } + // some of the target files are parents of the selected folder + targetFilePaths?.any { PathUtils.isAncestor(it, file.remotePath) } == true -> false + else -> true } private fun updateNavigationElementsInActionBar() { @@ -407,13 +422,17 @@ open class FolderPickerActivity : val atRoot = (currentDir == null || currentDir.parentId == 0L) actionBar.setDisplayHomeAsUpEnabled(!atRoot) actionBar.setHomeButtonEnabled(!atRoot) - val title = if (atRoot) captionText ?: "" else currentDir?.fileName - title?.let { - viewThemeUtils.files.themeActionBar(this, actionBar, title) + getSelectedFolderPathTitle()?.let { + viewThemeUtils.files.themeActionBar(this, actionBar, it) } } } + private fun getSelectedFolderPathTitle(): String? { + val atRoot = (currentDir == null || currentDir.parentId == 0L) + return if (atRoot) captionText ?: "" else currentDir?.fileName + } + private fun initControls() { if (this is FilePickerActivity) { viewThemeUtils.material.colorMaterialButtonPrimaryFilled(filesPickerBinding.folderPickerBtnCancel) @@ -441,6 +460,7 @@ open class FolderPickerActivity : } } + @Suppress("MagicNumber") private fun processOperation(action: String?) { val i = intent val resultData = Intent() @@ -475,10 +495,7 @@ open class FolderPickerActivity : * @param operation Creation operation performed. * @param result Result of the creation. */ - private fun onCreateFolderOperationFinish( - operation: CreateFolderOperation, - result: RemoteOperationResult<*> - ) { + private fun onCreateFolderOperationFinish(operation: CreateFolderOperation, result: RemoteOperationResult<*>) { if (result.isSuccess) { val fileListFragment = listOfFilesFragment fileListFragment?.onItemClicked(storageManager.getFileByPath(operation.remotePath)) @@ -532,9 +549,7 @@ open class FolderPickerActivity : return } - if (FileSyncAdapter.EVENT_FULL_SYNC_START == event) { - mSyncInProgress = true - } else { + if (FileSyncAdapter.EVENT_FULL_SYNC_START != event) { var (currentFile, currentDir) = getCurrentFileAndDirectory() if (currentDir == null) { @@ -550,23 +565,17 @@ open class FolderPickerActivity : file = currentFile } - mSyncInProgress = ( - FileSyncAdapter.EVENT_FULL_SYNC_END != event && - RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED != event - ) - - checkCredentials(syncResult, context, event) + checkCredentials(syncResult, event) } DataHolderUtil.getInstance().delete(intent.getStringExtra(FileSyncAdapter.EXTRA_RESULT)) - Log_OC.d(TAG, "Setting progress visibility to $mSyncInProgress") - listOfFilesFragment?.isLoading = mSyncInProgress - setBackgroundText() } catch (e: RuntimeException) { Log_OC.e(TAG, "Error on broadcast receiver", e) // avoid app crashes after changing the serial id of RemoteOperationResult // in owncloud library with broadcast notifications pending to process DataHolderUtil.getInstance().delete(intent.getStringExtra(FileSyncAdapter.EXTRA_RESULT)) + } finally { + listOfFilesFragment?.setEmptyListMessage(EmptyListState.LOCAL_FILE_LIST_EMPTY_FILE) } } @@ -594,15 +603,16 @@ open class FolderPickerActivity : browseToRoot() } - private fun checkCredentials(syncResult: RemoteOperationResult<*>, context: Context, event: String?) { + private fun checkCredentials(syncResult: RemoteOperationResult<*>, event: String?) { if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED == event && !syncResult.isSuccess ) { - if (ResultCode.UNAUTHORIZED == syncResult.code || ( + if (ResultCode.UNAUTHORIZED == syncResult.code || + ( syncResult.isException && syncResult.exception is AuthenticatorException ) ) { - requestCredentialsUpdate(context) + requestCredentialsUpdate() } else if (ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED == syncResult.code) { showUntrustedCertDialog(syncResult) } @@ -663,6 +673,8 @@ open class FolderPickerActivity : const val MOVE_OR_COPY = "MOVE_OR_COPY" const val CHOOSE_LOCATION = "CHOOSE_LOCATION" private val TAG = FolderPickerActivity::class.java.simpleName + private const val MOVE = "MOVE" + private const val COPY = "COPY" const val TAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS" } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt new file mode 100644 index 0000000..8ae77e6 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt @@ -0,0 +1,236 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Tobias Kaminsky + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.activity + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.ArrayAdapter +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.jobs.BackgroundJobManager +import com.nextcloud.client.jobs.download.FileDownloadWorker +import com.nextcloud.utils.extensions.hourPlural +import com.nextcloud.utils.extensions.minPlural +import com.nextcloud.utils.extensions.setVisibleIf +import com.owncloud.android.R +import com.owncloud.android.databinding.InternalTwoWaySyncLayoutBinding +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.ui.adapter.InternalTwoWaySyncAdapter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import javax.inject.Inject +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.minutes + +class InternalTwoWaySyncActivity : + DrawerActivity(), + Injectable, + InternalTwoWaySyncAdapter.InternalTwoWaySyncAdapterOnUpdate { + private val tag = "InternalTwoWaySyncActivity" + + @Inject + lateinit var backgroundJobManager: BackgroundJobManager + + lateinit var binding: InternalTwoWaySyncLayoutBinding + + private lateinit var internalTwoWaySyncAdapter: InternalTwoWaySyncAdapter + private var disableForAllFoldersMenuButton: MenuItem? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + internalTwoWaySyncAdapter = InternalTwoWaySyncAdapter(fileDataStorageManager, user.get(), this, this) + + binding = InternalTwoWaySyncLayoutBinding.inflate(layoutInflater) + setContentView(binding.root) + + setupToolbar() + setupActionBar() + setupTwoWaySyncAdapter() + setupEmptyList() + setupTwoWaySyncToggle() + setupTwoWaySyncInterval() + checkLayoutVisibilities(preferences.isTwoWaySyncEnabled) + } + + private fun setupActionBar() { + updateActionBarTitleAndHomeButtonByString(getString(R.string.two_way_sync_activity_title)) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + @SuppressLint("NotifyDataSetChanged") + private fun setupTwoWaySyncAdapter() { + if (preferences.isTwoWaySyncEnabled) { + binding.run { + list.run { + setEmptyView(emptyList.emptyListView) + adapter = internalTwoWaySyncAdapter + layoutManager = LinearLayoutManager(this@InternalTwoWaySyncActivity) + adapter?.notifyDataSetChanged() + } + } + } + } + + private fun setupEmptyList() { + binding.emptyList.run { + emptyListViewHeadline.run { + visibility = View.VISIBLE + setText(R.string.two_way_sync_activity_empty_list_title) + } + + emptyListViewText.run { + visibility = View.VISIBLE + setText(R.string.two_way_sync_activity_empty_list_desc) + } + + emptyListIcon.run { + visibility = View.VISIBLE + setImageDrawable( + viewThemeUtils.platform.tintDrawable( + context, + R.drawable.ic_sync, + ColorRole.PRIMARY + ) + ) + } + } + } + + @Suppress("TooGenericExceptionCaught") + private fun disableTwoWaySyncAndWorkers() { + lifecycleScope.launch(Dispatchers.IO) { + try { + backgroundJobManager.cancelTwoWaySyncJob() + + val currentUser = user.get() + + val folders = fileDataStorageManager.getInternalTwoWaySyncFolders(currentUser) + folders.forEach { folder -> + FileDownloadWorker.cancelOperation(currentUser.accountName, folder.fileId) + backgroundJobManager.cancelFilesDownloadJob(currentUser, folder.fileId) + + folder.internalFolderSyncTimestamp = -1L + fileDataStorageManager.saveFile(folder) + } + + withContext(Dispatchers.Main) { + internalTwoWaySyncAdapter.update() + } + } catch (e: Exception) { + Log_OC.d(tag, "Error caught at disableTwoWaySyncAndWorkers: $e") + } + } + } + + @Suppress("MagicNumber") + private fun setupTwoWaySyncInterval() { + val durations = listOf( + 15.minutes to minPlural(15), + 30.minutes to minPlural(30), + 45.minutes to minPlural(45), + 1.hours to hourPlural(1), + 2.hours to hourPlural(2), + 4.hours to hourPlural(4), + 6.hours to hourPlural(6), + 8.hours to hourPlural(8), + 12.hours to hourPlural(12), + 24.hours to hourPlural(24) + ) + val selectedDuration = durations.find { it.first.inWholeMinutes == preferences.twoWaySyncInterval } + + val adapter = ArrayAdapter( + this, + android.R.layout.simple_dropdown_item_1line, + durations.map { it.second } + ) + + binding.twoWaySyncInterval.run { + setAdapter(adapter) + setText(selectedDuration?.second ?: minPlural(15), false) + setOnItemClickListener { _, _, position, _ -> + handleDurationSelected(durations[position].first.inWholeMinutes) + } + } + } + + private fun handleDurationSelected(duration: Long) { + preferences.twoWaySyncInterval = duration + backgroundJobManager.scheduleInternal2WaySync(duration) + } + + private fun setupTwoWaySyncToggle() { + binding.twoWaySyncToggle.isChecked = preferences.isTwoWaySyncEnabled + binding.twoWaySyncToggle.setOnCheckedChangeListener { _, isChecked -> + preferences.setTwoWaySyncStatus(isChecked) + setupTwoWaySyncAdapter() + checkLayoutVisibilities(isChecked) + checkDisableForAllFoldersMenuButtonVisibility() + + if (isChecked) { + backgroundJobManager.scheduleInternal2WaySync(preferences.twoWaySyncInterval) + } else { + backgroundJobManager.cancelTwoWaySyncJob() + } + } + } + + private fun checkLayoutVisibilities(condition: Boolean) { + binding.listFrameLayout.setVisibleIf(condition) + binding.twoWaySyncIntervalLayout.setVisibleIf(condition) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.activity_internal_two_way_sync, menu) + disableForAllFoldersMenuButton = menu?.findItem(R.id.action_dismiss_two_way_sync) + checkDisableForAllFoldersMenuButtonVisibility() + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + onBackPressed() + } + R.id.action_dismiss_two_way_sync -> { + disableTwoWaySyncAndWorkers() + } + } + + return super.onOptionsItemSelected(item) + } + + private fun checkDisableForAllFoldersMenuButtonVisibility() { + lifecycleScope.launch { + val folderSize = withContext(Dispatchers.IO) { + fileDataStorageManager.getInternalTwoWaySyncFolders(user.get()).size + } + + checkDisableForAllFoldersMenuButtonVisibility(preferences.isTwoWaySyncEnabled, folderSize) + } + } + + private fun checkDisableForAllFoldersMenuButtonVisibility(isTwoWaySyncEnabled: Boolean, folderSize: Int) { + val showDisableButton = isTwoWaySyncEnabled && folderSize > 0 + + disableForAllFoldersMenuButton?.let { + it.setVisible(showDisableButton) + it.setEnabled(showDisableButton) + } + } + + override fun onUpdate(folderSize: Int) { + checkDisableForAllFoldersMenuButtonVisibility(preferences.isTwoWaySyncEnabled, folderSize) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java deleted file mode 100644 index 03492fa..0000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-FileCopyrightText: 2020 Chawki Chouib - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-FileCopyrightText: 2016-2018 Andy Scherzinger - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later - */ -package com.owncloud.android.ui.activity; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; -import android.accounts.OperationCanceledException; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.Handler; -import android.view.MenuItem; -import android.view.View; - -import com.google.common.collect.Sets; -import com.nextcloud.client.account.User; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.jobs.BackgroundJobManager; -import com.nextcloud.client.jobs.download.FileDownloadHelper; -import com.nextcloud.client.onboarding.FirstRunActivity; -import com.nextcloud.model.WorkerState; -import com.nextcloud.model.WorkerStateLiveData; -import com.nextcloud.utils.extensions.BundleExtensionsKt; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.authentication.AuthenticatorActivity; -import com.owncloud.android.datamodel.ArbitraryDataProvider; -import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.UserInfo; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.operations.DownloadFileOperation; -import com.owncloud.android.services.OperationsService; -import com.owncloud.android.ui.adapter.UserListAdapter; -import com.owncloud.android.ui.adapter.UserListItem; -import com.owncloud.android.ui.dialog.AccountRemovalDialog; -import com.owncloud.android.ui.events.AccountRemovedEvent; -import com.owncloud.android.ui.helpers.FileOperationsHelper; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import javax.inject.Inject; - -import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.PopupMenu; -import androidx.fragment.app.FragmentManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import static com.owncloud.android.ui.activity.UserInfoActivity.KEY_USER_DATA; -import static com.owncloud.android.ui.adapter.UserListAdapter.KEY_DISPLAY_NAME; -import static com.owncloud.android.ui.adapter.UserListAdapter.KEY_USER_INFO_REQUEST_CODE; - -/** - * An Activity that allows the user to manage accounts. - */ -public class ManageAccountsActivity extends FileActivity implements UserListAdapter.Listener, - AccountManagerCallback, - ComponentsGetter, - UserListAdapter.ClickListener { - private static final String TAG = ManageAccountsActivity.class.getSimpleName(); - - public static final String KEY_ACCOUNT_LIST_CHANGED = "ACCOUNT_LIST_CHANGED"; - public static final String KEY_CURRENT_ACCOUNT_CHANGED = "CURRENT_ACCOUNT_CHANGED"; - public static final String PENDING_FOR_REMOVAL = UserAccountManager.PENDING_FOR_REMOVAL; - - private static final int KEY_DELETE_CODE = 101; - private static final int SINGLE_ACCOUNT = 1; - private static final int MIN_MULTI_ACCOUNT_SIZE = 2; - - private RecyclerView recyclerView; - private final Handler handler = new Handler(); - private String accountName; - private UserListAdapter userListAdapter; - private Set originalUsers; - private String originalCurrentUser; - - private ArbitraryDataProvider arbitraryDataProvider; - private boolean multipleAccountsSupported; - - private String workerAccountName; - private DownloadFileOperation workerCurrentDownload; - - @Inject BackgroundJobManager backgroundJobManager; - @Inject UserAccountManager accountManager; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.accounts_layout); - - recyclerView = findViewById(R.id.account_list); - - setupToolbar(); - - // set the back button from action bar - ActionBar actionBar = getSupportActionBar(); - - // check if is not null - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setDisplayShowHomeEnabled(true); - viewThemeUtils.files.themeActionBar(this, actionBar, R.string.prefs_manage_accounts); - } - - List users = accountManager.getAllUsers(); - originalUsers = toAccountNames(users); - - Optional currentUser = getUser(); - if (currentUser.isPresent()) { - originalCurrentUser = currentUser.get().getAccountName(); - } - - arbitraryDataProvider = new ArbitraryDataProviderImpl(this); - - multipleAccountsSupported = getResources().getBoolean(R.bool.multiaccount_support); - - userListAdapter = new UserListAdapter(this, - accountManager, - getUserListItems(), - this, - multipleAccountsSupported, - true, - true, - viewThemeUtils); - - recyclerView.setAdapter(userListAdapter); - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - observeWorkerState(); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == KEY_DELETE_CODE && data != null) { - Bundle bundle = data.getExtras(); - if (bundle != null && bundle.containsKey(UserInfoActivity.KEY_ACCOUNT)) { - final Account account = BundleExtensionsKt.getParcelableArgument(bundle, UserInfoActivity.KEY_ACCOUNT, Account.class); - if (account != null) { - User user = accountManager.getUser(account.name).orElseThrow(RuntimeException::new); - accountName = account.name; - performAccountRemoval(user); - } - } - } - } - - @Override - public void onBackPressed() { - Intent resultIntent = new Intent(); - if (accountManager.getAllUsers().size() > 0) { - resultIntent.putExtra(KEY_ACCOUNT_LIST_CHANGED, hasAccountListChanged()); - resultIntent.putExtra(KEY_CURRENT_ACCOUNT_CHANGED, hasCurrentAccountChanged()); - setResult(RESULT_OK, resultIntent); - - super.onBackPressed(); - } else { - final Intent intent = new Intent(this, AuthenticatorActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - finish(); - } - } - - /** - * checks the set of actual accounts against the set of original accounts when the activity has been started. - * - * @return true if account list has changed, false if not - */ - private boolean hasAccountListChanged() { - List users = accountManager.getAllUsers(); - List newList = new ArrayList<>(); - for (User user : users) { - boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(user, PENDING_FOR_REMOVAL); - - if (!pendingForRemoval) { - newList.add(user); - } - } - Set actualAccounts = toAccountNames(newList); - return !originalUsers.equals(actualAccounts); - } - - private static Set toAccountNames(Collection users) { - Set accountNames = Sets.newHashSetWithExpectedSize(users.size()); - for (User user : users) { - accountNames.add(user.getAccountName()); - } - return accountNames; - } - - /** - * checks actual current account against current accounts when the activity has been started. - * - * @return true if account list has changed, false if not - */ - private boolean hasCurrentAccountChanged() { - User user = getUserAccountManager().getUser(); - if (user.isAnonymous()) { - return true; - } else { - return !user.getAccountName().equals(originalCurrentUser); - } - } - - private List getUserListItems() { - List users = accountManager.getAllUsers(); - List userListItems = new ArrayList<>(users.size()); - for (User user : users) { - boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(user, PENDING_FOR_REMOVAL); - userListItems.add(new UserListItem(user, !pendingForRemoval)); - } - - if (getResources().getBoolean(R.bool.multiaccount_support)) { - userListItems.add(new UserListItem()); - } - - return userListItems; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean retval = true; - - if (item.getItemId() == android.R.id.home) { - onBackPressed(); - } else { - retval = super.onOptionsItemSelected(item); - } - - return retval; - } - - @Override - public void showFirstRunActivity() { - Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class); - firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true); - startActivity(firstRunIntent); - } - - @Override - public void startAccountCreation() { - AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(this), - null, - null, - null, - this, - future -> { - if (future != null) { - try { - Bundle result = future.getResult(); - String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); - accountManager.setCurrentOwnCloudAccount(name); - userListAdapter = new UserListAdapter( - this, - accountManager, - getUserListItems(), - this, - multipleAccountsSupported, - false, - true, - viewThemeUtils); - recyclerView.setAdapter(userListAdapter); - runOnUiThread(() -> userListAdapter.notifyDataSetChanged()); - } catch (OperationCanceledException e) { - Log_OC.d(TAG, "Account creation canceled"); - } catch (Exception e) { - Log_OC.e(TAG, "Account creation finished in exception: ", e); - } - } - }, handler); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAccountRemovedEvent(AccountRemovedEvent event) { - List userListItemArray = getUserListItems(); - userListAdapter.clear(); - userListAdapter.addAll(userListItemArray); - userListAdapter.notifyDataSetChanged(); - } - - @Override - public void run(AccountManagerFuture future) { - if (future.isDone()) { - // after remove account - Optional user = accountManager.getUser(accountName); - if (!user.isPresent()) { - fileUploadHelper.cancel(accountName); - FileDownloadHelper.Companion.instance().cancelAllDownloadsForAccount(workerAccountName, workerCurrentDownload); - } - - User currentUser = getUserAccountManager().getUser(); - if (currentUser.isAnonymous()) { - String accountName = ""; - List users = accountManager.getAllUsers(); - if (users.size() > 0) { - accountName = users.get(0).getAccountName(); - } - accountManager.setCurrentOwnCloudAccount(accountName); - } - - List userListItemArray = getUserListItems(); - if (userListItemArray.size() > SINGLE_ACCOUNT) { - userListAdapter = new UserListAdapter(this, - accountManager, - userListItemArray, - this, - multipleAccountsSupported, - false, - true, - viewThemeUtils); - recyclerView.setAdapter(userListAdapter); - } else { - onBackPressed(); - } - } - } - - public Handler getHandler() { - return handler; - } - - @Override - public OperationsService.OperationsServiceBinder getOperationsServiceBinder() { - return null; - } - - @Override - public FileDataStorageManager getStorageManager() { - return super.getStorageManager(); - } - - @Override - public FileOperationsHelper getFileOperationsHelper() { - return null; - } - - private void performAccountRemoval(User user) { - // disable account in recycler view - for (int i = 0; i < userListAdapter.getItemCount(); i++) { - UserListItem item = userListAdapter.getItem(i); - - if (item != null && item.getUser().getAccountName().equalsIgnoreCase(user.getAccountName())) { - item.setEnabled(false); - break; - } - - userListAdapter.notifyDataSetChanged(); - } - - // store pending account removal - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this); - arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PENDING_FOR_REMOVAL, String.valueOf(true)); - - FileDownloadHelper.Companion.instance().cancelAllDownloadsForAccount(workerAccountName, workerCurrentDownload); - fileUploadHelper.cancel(user.getAccountName()); - backgroundJobManager.startAccountRemovalJob(user.getAccountName(), false); - - // immediately select a new account - List users = accountManager.getAllUsers(); - - String newAccountName = ""; - for (User u : users) { - if (!u.getAccountName().equalsIgnoreCase(u.getAccountName())) { - newAccountName = u.getAccountName(); - break; - } - } - - if (newAccountName.isEmpty()) { - Log_OC.d(TAG, "new account set to null"); - accountManager.resetOwnCloudAccount(); - } else { - Log_OC.d(TAG, "new account set to: " + newAccountName); - accountManager.setCurrentOwnCloudAccount(newAccountName); - } - - // only one to be (deleted) account remaining - if (users.size() < MIN_MULTI_ACCOUNT_SIZE) { - Intent resultIntent = new Intent(); - resultIntent.putExtra(KEY_ACCOUNT_LIST_CHANGED, true); - resultIntent.putExtra(KEY_CURRENT_ACCOUNT_CHANGED, true); - setResult(RESULT_OK, resultIntent); - - super.onBackPressed(); - } - } - - public static void openAccountRemovalDialog(User user, FragmentManager fragmentManager) { - AccountRemovalDialog dialog = AccountRemovalDialog.newInstance(user); - dialog.show(fragmentManager, "dialog"); - } - - private void openAccount(User user) { - final Intent intent = new Intent(this, UserInfoActivity.class); - intent.putExtra(UserInfoActivity.KEY_ACCOUNT, user); - OwnCloudAccount oca = user.toOwnCloudAccount(); - intent.putExtra(KEY_DISPLAY_NAME, oca.getDisplayName()); - startActivityForResult(intent, KEY_USER_INFO_REQUEST_CODE); - } - - @VisibleForTesting - public void showUser(User user, UserInfo userInfo) { - final Intent intent = new Intent(this, UserInfoActivity.class); - OwnCloudAccount oca = user.toOwnCloudAccount(); - intent.putExtra(UserInfoActivity.KEY_ACCOUNT, user); - intent.putExtra(KEY_DISPLAY_NAME, oca.getDisplayName()); - intent.putExtra(KEY_USER_DATA, userInfo); - startActivityForResult(intent, KEY_USER_INFO_REQUEST_CODE); - } - - @Override - public void onOptionItemClicked(User user, View view) { - if (view.getId() == R.id.account_menu) { - PopupMenu popup = new PopupMenu(this, view); - popup.getMenuInflater().inflate(R.menu.item_account, popup.getMenu()); - - if (accountManager.getUser().equals(user)) { - popup.getMenu().findItem(R.id.action_open_account).setVisible(false); - } - popup.setOnMenuItemClickListener(item -> { - int itemId = item.getItemId(); - - if (itemId == R.id.action_open_account) { - accountClicked(user.hashCode()); - } else if (itemId == R.id.action_delete_account) { - openAccountRemovalDialog(user, getSupportFragmentManager()); - } else { - openAccount(user); - } - - return true; - }); - popup.show(); - } else { - openAccount(user); - } - } - - private void observeWorkerState() { - WorkerStateLiveData.Companion.instance().observe(this, state -> { - if (state instanceof WorkerState.Download) { - Log_OC.d(TAG, "Download worker started"); - workerAccountName = ((WorkerState.Download) state).getUser().getAccountName(); - workerCurrentDownload = ((WorkerState.Download) state).getCurrentDownload(); - } - }); - } - - @Override - public void onAccountClicked(User user) { - openAccount(user); - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt new file mode 100644 index 0000000..65c4d07 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt @@ -0,0 +1,497 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Chris Narkiewicz + * SPDX-FileCopyrightText: 2020 Chawki Chouib + * SPDX-FileCopyrightText: 2019 Tobias Kaminsky + * SPDX-FileCopyrightText: 2016-2018 Andy Scherzinger + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + */ +package com.owncloud.android.ui.activity + +import android.accounts.Account +import android.accounts.AccountManager +import android.accounts.AccountManagerCallback +import android.accounts.AccountManagerFuture +import android.accounts.OperationCanceledException +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.view.MenuItem +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.annotation.VisibleForTesting +import androidx.appcompat.widget.PopupMenu +import androidx.fragment.app.FragmentManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.common.collect.Sets +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.jobs.download.FileDownloadHelper +import com.nextcloud.client.onboarding.FirstRunActivity +import com.nextcloud.model.WorkerState +import com.nextcloud.model.WorkerState.DownloadStarted +import com.nextcloud.model.WorkerStateLiveData +import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.mdm.MDMConfig.multiAccountSupport +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.authentication.AuthenticatorActivity +import com.owncloud.android.datamodel.ArbitraryDataProvider +import com.owncloud.android.datamodel.ArbitraryDataProviderImpl +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.UserInfo +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.DownloadFileOperation +import com.owncloud.android.services.OperationsService.OperationsServiceBinder +import com.owncloud.android.ui.adapter.UserListAdapter +import com.owncloud.android.ui.adapter.UserListItem +import com.owncloud.android.ui.dialog.AccountRemovalDialog.Companion.newInstance +import com.owncloud.android.ui.events.AccountRemovedEvent +import com.owncloud.android.ui.helpers.FileOperationsHelper +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +/** + * An Activity that allows the user to manage accounts. + */ +class ManageAccountsActivity : + FileActivity(), + UserListAdapter.Listener, + AccountManagerCallback, + ComponentsGetter, + UserListAdapter.ClickListener { + + private var recyclerView: RecyclerView? = null + private val handler = Handler() + private var accountName: String? = null + private var userListAdapter: UserListAdapter? = null + private var originalUsers: Set? = null + private var originalCurrentUser: String? = null + + private var multipleAccountsSupported = false + + private var workerAccountName: String? = null + private var workerCurrentDownload: DownloadFileOperation? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.accounts_layout) + + setupToolbar() + setupActionBar() + setupUsers() + + @Suppress("DEPRECATION") + arbitraryDataProvider = ArbitraryDataProviderImpl(this) + multipleAccountsSupported = multiAccountSupport(this) + + setupUserList() + handleOnBackPressed() + } + + private fun setupUsers() { + val users = accountManager.allUsers + originalUsers = toAccountNames(users) + + user.ifPresent { + originalCurrentUser = user.get().accountName + } + } + + private fun setupActionBar() { + supportActionBar?.let { + it.setDisplayHomeAsUpEnabled(true) + it.setDisplayShowHomeEnabled(true) + viewThemeUtils.files.themeActionBar(this, it, R.string.prefs_manage_accounts) + } + } + + private fun setupUserList() { + userListAdapter = UserListAdapter( + this, + accountManager, + userListItems, + this, + multipleAccountsSupported, + true, + true, + viewThemeUtils + ) + + recyclerView = findViewById(R.id.account_list) + recyclerView?.setAdapter(userListAdapter) + recyclerView?.setLayoutManager(LinearLayoutManager(this)) + observeWorkerState() + } + + @Suppress("ReturnCount") + @Deprecated("Use ActivityResultLauncher") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + if (resultCode != KEY_DELETE_CODE || data == null) { + return + } + + val bundle = data.extras + if (bundle == null || !bundle.containsKey(UserInfoActivity.KEY_ACCOUNT)) { + return + } + + val account = bundle.getParcelableArgument(UserInfoActivity.KEY_ACCOUNT, Account::class.java) ?: return + val user = accountManager.getUser(account.name).orElseThrow { RuntimeException() } + accountName = account.name + performAccountRemoval(user) + } + + private fun handleOnBackPressed() { + onBackPressedDispatcher.addCallback( + this, + onBackPressedCallback + ) + } + + private val onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val resultIntent = Intent() + + if (accountManager.allUsers.size > 0) { + resultIntent.putExtra(KEY_ACCOUNT_LIST_CHANGED, hasAccountListChanged()) + resultIntent.putExtra(KEY_CURRENT_ACCOUNT_CHANGED, hasCurrentAccountChanged()) + setResult(RESULT_OK, resultIntent) + } else { + val intent = Intent(this@ManageAccountsActivity, AuthenticatorActivity::class.java) + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(intent) + } + + finish() + } + } + + /** + * checks the set of actual accounts against the set of original accounts when the activity has been started. + * + * @return true if account list has changed, false if not + */ + private fun hasAccountListChanged(): Boolean { + val users = accountManager.allUsers + val newList: MutableList = ArrayList() + for (user in users) { + val pendingForRemoval = arbitraryDataProvider.getBooleanValue(user, PENDING_FOR_REMOVAL) + + if (!pendingForRemoval) { + newList.add(user) + } + } + val actualAccounts = toAccountNames(newList) + return originalUsers != actualAccounts + } + + /** + * checks actual current account against current accounts when the activity has been started. + * + * @return true if account list has changed, false if not + */ + private fun hasCurrentAccountChanged(): Boolean { + val user = userAccountManager.user + return if (user.isAnonymous) { + true + } else { + user.accountName != originalCurrentUser + } + } + + private val userListItems: List + get() { + val users = accountManager.allUsers + val userListItems: MutableList = + ArrayList(users.size) + for (user in users) { + val pendingForRemoval = + arbitraryDataProvider.getBooleanValue(user, PENDING_FOR_REMOVAL) + userListItems.add(UserListItem(user, !pendingForRemoval)) + } + + if (multiAccountSupport(this)) { + userListItems.add(UserListItem()) + } + + return userListItems + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + var result = true + + if (item.itemId == android.R.id.home) { + onBackPressed() + } else { + result = super.onOptionsItemSelected(item) + } + + return result + } + + override fun showFirstRunActivity() { + val intent = Intent(applicationContext, FirstRunActivity::class.java).apply { + putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true) + } + startActivity(intent) + } + + @Suppress("TooGenericExceptionCaught") + @SuppressLint("NotifyDataSetChanged") + override fun startAccountCreation() { + val am = AccountManager.get(applicationContext) + am.addAccount( + MainApp.getAccountType(this), + null, + null, + null, + this, + { future: AccountManagerFuture? -> + if (future != null) { + try { + val result = future.result + val name = result.getString(AccountManager.KEY_ACCOUNT_NAME) + accountManager.setCurrentOwnCloudAccount(name) + userListAdapter = UserListAdapter( + this, + accountManager, + userListItems, + this, + multipleAccountsSupported, + false, + true, + viewThemeUtils + ) + recyclerView?.adapter = userListAdapter + runOnUiThread { userListAdapter?.notifyDataSetChanged() } + } catch (e: OperationCanceledException) { + Log_OC.d(TAG, "Account creation canceled") + } catch (e: Exception) { + Log_OC.e(TAG, "Account creation finished in exception: ", e) + } + } + }, + handler + ) + } + + @SuppressLint("NotifyDataSetChanged") + @Subscribe(threadMode = ThreadMode.MAIN) + override fun onAccountRemovedEvent(event: AccountRemovedEvent) { + val userListItemArray = userListItems + userListAdapter?.clear() + userListAdapter?.addAll(userListItemArray) + userListAdapter?.notifyDataSetChanged() + } + + override fun run(future: AccountManagerFuture) { + if (!future.isDone) { + return + } + + // after remove account + accountName?.let { + val user = accountManager.getUser(it) + + if (!user.isPresent) { + fileUploadHelper.cancel(it) + FileDownloadHelper.instance().cancelAllDownloadsForAccount(workerAccountName, workerCurrentDownload) + } + } + + val currentUser = userAccountManager.user + if (currentUser.isAnonymous) { + var accountName = "" + val users = accountManager.allUsers + if (users.size > 0) { + accountName = users[0].accountName + } + accountManager.setCurrentOwnCloudAccount(accountName) + } + + val userListItemArray = userListItems + if (userListItemArray.size > SINGLE_ACCOUNT) { + userListAdapter = UserListAdapter( + this, + accountManager, + userListItemArray, + this, + multipleAccountsSupported, + false, + true, + viewThemeUtils + ) + recyclerView?.adapter = userListAdapter + } else { + onBackPressed() + } + } + + override fun getHandler(): Handler = handler + + override fun getOperationsServiceBinder(): OperationsServiceBinder? = null + + override fun getStorageManager(): FileDataStorageManager = super.getStorageManager() + + override fun getFileOperationsHelper(): FileOperationsHelper? = null + + @Suppress("DEPRECATION") + @SuppressLint("NotifyDataSetChanged") + private fun performAccountRemoval(user: User) { + val itemCount = userListAdapter?.itemCount ?: 0 + + // disable account in recycler view + for (i in 0 until itemCount) { + val item = userListAdapter?.getItem(i) + + if (item != null && item.user.accountName.equals(user.accountName, ignoreCase = true)) { + item.isEnabled = false + break + } + + userListAdapter?.notifyDataSetChanged() + } + + // store pending account removal + val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(this) + arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, PENDING_FOR_REMOVAL, true.toString()) + + FileDownloadHelper.instance().cancelAllDownloadsForAccount(workerAccountName, workerCurrentDownload) + fileUploadHelper.cancel(user.accountName) + backgroundJobManager.startAccountRemovalJob(user.accountName, false) + + // immediately select a new account + val users = accountManager.allUsers + + var newAccountName = "" + for (u in users) { + if (!u.accountName.equals(u.accountName, ignoreCase = true)) { + newAccountName = u.accountName + break + } + } + + if (newAccountName.isEmpty()) { + Log_OC.d(TAG, "new account set to null") + accountManager.resetOwnCloudAccount() + } else { + Log_OC.d(TAG, "new account set to: $newAccountName") + accountManager.setCurrentOwnCloudAccount(newAccountName) + } + + // only one to be (deleted) account remaining + if (users.size < MIN_MULTI_ACCOUNT_SIZE) { + val resultIntent = Intent() + resultIntent.putExtra(KEY_ACCOUNT_LIST_CHANGED, true) + resultIntent.putExtra(KEY_CURRENT_ACCOUNT_CHANGED, true) + setResult(RESULT_OK, resultIntent) + + super.onBackPressed() + } + } + + @Suppress("DEPRECATION") + private fun openAccount(user: User) { + val intent = Intent(this, UserInfoActivity::class.java).apply { + putExtra(UserInfoActivity.KEY_ACCOUNT, user) + + val oca = user.toOwnCloudAccount() + putExtra(UserListAdapter.KEY_DISPLAY_NAME, oca.displayName) + } + + startActivityForResult(intent, UserListAdapter.KEY_USER_INFO_REQUEST_CODE) + } + + @Suppress("DEPRECATION") + @VisibleForTesting + fun showUser(user: User, userInfo: UserInfo?) { + val intent = Intent(this, UserInfoActivity::class.java).apply { + val oca = user.toOwnCloudAccount() + putExtra(UserInfoActivity.KEY_ACCOUNT, user) + putExtra(UserListAdapter.KEY_DISPLAY_NAME, oca.displayName) + putExtra(UserInfoActivity.KEY_USER_DATA, userInfo) + } + + startActivityForResult(intent, UserListAdapter.KEY_USER_INFO_REQUEST_CODE) + } + + override fun onOptionItemClicked(user: User, view: View) { + if (view.id == R.id.account_menu) { + val popup = PopupMenu(this, view) + popup.menuInflater.inflate(R.menu.item_account, popup.menu) + + if (accountManager.user == user) { + popup.menu.findItem(R.id.action_open_account).setVisible(false) + } + + popup.setOnMenuItemClickListener { item: MenuItem -> + val itemId = item.itemId + when (itemId) { + R.id.action_open_account -> { + accountClicked(user.hashCode()) + } + R.id.action_delete_account -> { + openAccountRemovalDialog(user, supportFragmentManager) + } + else -> { + openAccount(user) + } + } + true + } + + popup.show() + } else { + openAccount(user) + } + } + + private fun observeWorkerState() { + WorkerStateLiveData.instance().observe( + this + ) { state: WorkerState? -> + if (state is DownloadStarted) { + Log_OC.d(TAG, "Download worker started") + workerAccountName = state.user?.accountName + workerCurrentDownload = state.currentDownload + } + } + } + + override fun onAccountClicked(user: User) { + openAccount(user) + } + + companion object { + private val TAG: String = ManageAccountsActivity::class.java.simpleName + + const val KEY_ACCOUNT_LIST_CHANGED: String = "ACCOUNT_LIST_CHANGED" + const val KEY_CURRENT_ACCOUNT_CHANGED: String = "CURRENT_ACCOUNT_CHANGED" + const val PENDING_FOR_REMOVAL: String = UserAccountManager.PENDING_FOR_REMOVAL + + private const val KEY_DELETE_CODE = 101 + private const val SINGLE_ACCOUNT = 1 + private const val MIN_MULTI_ACCOUNT_SIZE = 2 + + private fun toAccountNames(users: Collection): Set { + val accountNames: MutableSet = Sets.newHashSetWithExpectedSize(users.size) + for (user in users) { + accountNames.add(user.accountName) + } + return accountNames + } + + fun openAccountRemovalDialog(user: User, fragmentManager: FragmentManager) { + val dialog = newInstance(user) + dialog.show(fragmentManager, "dialog") + } + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.kt index 909dd0d..510d5be 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.kt @@ -1,28 +1,34 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity -import android.os.AsyncTask import android.os.Bundle import android.view.MenuItem -import android.widget.Button -import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.Snackbar +import com.nextcloud.android.common.ui.util.extensions.applyEdgeToEdgeWithSystemBarPadding import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable import com.nextcloud.client.preferences.AppPreferences import com.owncloud.android.R +import com.owncloud.android.databinding.ActivityManageSpaceBinding import com.owncloud.android.lib.common.utils.Log_OC +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File import javax.inject.Inject import kotlin.system.exitProcess -class ManageSpaceActivity : AppCompatActivity(), Injectable { +class ManageSpaceActivity : + AppCompatActivity(), + Injectable { @Inject lateinit var preferences: AppPreferences @@ -30,44 +36,31 @@ class ManageSpaceActivity : AppCompatActivity(), Injectable { @Inject lateinit var userAccountManager: UserAccountManager - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_manage_space) - val actionBar = supportActionBar - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true) - actionBar.setTitle(R.string.manage_space_title) - } - val descriptionTextView = findViewById(R.id.general_description) - descriptionTextView.text = getString(R.string.manage_space_description, getString(R.string.app_name)) - val clearDataButton = findViewById

    B$1Gn8>Oy? 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png deleted file mode 100644 index 4b8e5ba28034728c574b19a0f2a0751898e62fe8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31094 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 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png deleted file mode 100644 index bba71fe6886dd6ae0cbc5aa57022815b4b9b731a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15155 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 WJuq4u0~Et - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only ~ ~ UI Automator requires Android 4.3 (API level 18) or higher. ~ So this AndroidManifest will be merged with the normal one (not requiring to change the minSdk) diff --git a/app/src/androidTest/assets/credentials.json b/app/src/androidTest/assets/credentials.json new file mode 100644 index 0000000..8e66049 --- /dev/null +++ b/app/src/androidTest/assets/credentials.json @@ -0,0 +1,4 @@ +{ + "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3wSNveXIhRsKl86pUnL7\n/AIAH+IJya5vqP0lv+yCBkd728szrLRYRWxPNC4VDbzyRHBr0RWj0ibsLJvU2OeF\n5p4er1tMIGgB0AEwiuDXBBz/RrxjPdhlilq7mvvqeUS2M3t5iroIxM6VEGQrhVrb\nb3U+7c6Lt7dIHAHEVOXnZiHYhhhduEmIzbsrAZFuMjlnWXTiMhuuWBf6t1nPyCHa\noA96loWibbvIsMegC73J3Ej5sgLkz/TjlrYmv6p3RGAEs74KHfggy4Fzw9TxBAAY\nyIX0NY8Rhb10XKrOSXrvRYuL/wkJ3P5XVK/NfsuLKbrhuUjDSgKplY9xCtOSaEPJ\nVQIDAQAB\n-----END PUBLIC KEY-----", + "certificate": "-----BEGIN CERTIFICATE-----\nMIIC9DCCAdygAwIBAgIBADANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDDAhuYXJy\nYXRvcjAeFw0yNDA1MjcxMzEyNDVaFw00NDA1MjIxMzEyNDVaMBMxETAPBgNVBAMM\nCG5hcnJhdG9yMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjA2EEYeN\nc3BdVDPkJK/AWPB1kd9sWAonZt/V4sbAE6fGy4qU21xfInZQaMHyhqdMXga10juE\nJLPKuyyRz+qijASryW+WzCJ3A9QeHHO+CiLc09yuB80JRpH0oHsol6WrdO1n5zuH\nlPtAdCwi4OeRmvazfBysbP2gaUl7DxackqbMei8a0MoyDxUB11hp0tpyYAU1/sXZ\nLGh4R4q4/F2KlSeYY9D62OJ8wNTgv9AYF/HRxXxWmVftB1En/DdvVr1zJGraHiRm\nQbaEnmsSGK8QHHm4h37cfD5f7rW1WO5A8KyJKwluOIXjMfL1YijAPpNW6EHhSlfT\n5RVLCHxvrzMHewIDAQABo1MwUTAdBgNVHQ4EFgQUzT6RHEHtpdjr8N3ABJK0wpFt\n1PMwHwYDVR0jBBgwFoAUzT6RHEHtpdjr8N3ABJK0wpFt1PMwDwYDVR0TAQH/BAUw\nAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAJ1q3CSBHrLauOZAD56BeElgh/ahbegsE\nZ4w7q4FdhkixLIwe6yrMmSvpNTuxRDHUrVLXxQmN0X3Yb7BLNXnnIUfH9EozaV7p\nYjOLWD2XCfLJmpGIBVvqZhyZrTl69jkBaVHF78aj1vt+qKihHUAVnG+qGH0PFms+\nG0KyY8bNYg+2HQiSTva1kgGPUA/8nQNj3lwi+r03tgqbw88fQKRPeMUJWdh/yV9U\noBdPHt+TBsUFZQZP3lBBS9lYhDT9fNoGX12WPAEUjYNhHVX+Qdup8Mg3aUMITXXJ\nvlGsN1SknlLoN0RwBFbyH9BCzqAdEIj5qQM3YDzIIyyy6AAnswNEUg==\n-----END CERTIFICATE-----" +} diff --git a/app/src/androidTest/disabledTests/uiautomator/InitialTest.java b/app/src/androidTest/disabledTests/uiautomator/InitialTest.java index 6f28594..b548770 100644 --- a/app/src/androidTest/disabledTests/uiautomator/InitialTest.java +++ b/app/src/androidTest/disabledTests/uiautomator/InitialTest.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. - * SPDX-License-Identifier: GPL-2.0-only AND AGPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ package com.owncloud.android.uiautomator; diff --git a/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt index 1d8bb85..8c3157c 100644 --- a/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client diff --git a/app/src/androidTest/java/com/nextcloud/client/AuthenticatorActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/AuthenticatorActivityIT.java index 4bd1121..4c704f0 100644 --- a/app/src/androidTest/java/com/nextcloud/client/AuthenticatorActivityIT.java +++ b/app/src/androidTest/java/com/nextcloud/client/AuthenticatorActivityIT.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2021 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client; diff --git a/app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.java deleted file mode 100644 index 2fbf002..0000000 --- a/app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.nextcloud.client; - -import android.app.Activity; - -import com.nextcloud.test.GrantStoragePermissionRule; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.ui.activity.CommunityActivity; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - - -public class CommunityActivityIT extends AbstractIT { - @Rule public IntentsTestRule activityRule = new IntentsTestRule<>(CommunityActivity.class, - true, - false); - - @Rule - public final TestRule permissionRule = GrantStoragePermissionRule.grant(); - - @Test - @ScreenshotTest - public void open() { - Activity sut = activityRule.launchActivity(null); - - screenshot(sut); - } -} diff --git a/app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.kt new file mode 100644 index 0000000..d44962b --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/client/CommunityActivityIT.kt @@ -0,0 +1,59 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2019 Tobias Kaminsky + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.client + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.test.GrantStoragePermissionRule.Companion.grant +import com.owncloud.android.AbstractIT +import com.owncloud.android.ui.activity.CommunityActivity +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule + +class CommunityActivityIT : AbstractIT() { + private val testClassName = "com.nextcloud.client.CommunityActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @get:Rule + var storagePermissionRule: TestRule = grant() + + @Test + @UiThread + @ScreenshotTest + fun open() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/nextcloud/client/EndToEndAction.java b/app/src/androidTest/java/com/nextcloud/client/EndToEndAction.java index 937cb57..08fab8c 100644 --- a/app/src/androidTest/java/com/nextcloud/client/EndToEndAction.java +++ b/app/src/androidTest/java/com/nextcloud/client/EndToEndAction.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client; diff --git a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt index 94a8f9f..ad6c28f 100644 --- a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt @@ -3,26 +3,26 @@ * * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ + package com.nextcloud.client -import android.app.Activity +import androidx.test.core.app.launchActivity import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.closeSoftKeyboard import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.DrawerActions import androidx.test.espresso.contrib.NavigationViewActions import androidx.test.espresso.contrib.RecyclerViewActions -import androidx.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.platform.app.InstrumentationRegistry import com.nextcloud.test.RetryTestRule import com.owncloud.android.AbstractOnServerIT import com.owncloud.android.R @@ -35,33 +35,39 @@ import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.operations.CreateFolderOperation import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.adapter.OCFileListItemViewHolder -import org.junit.Assert +import com.owncloud.android.utils.EspressoIdlingResource +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before import org.junit.Rule import org.junit.Test class FileDisplayActivityIT : AbstractOnServerIT() { - @get:Rule - val activityRule = IntentsTestRule( - FileDisplayActivity::class.java, - true, - false - ) + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @get:Rule val retryRule = RetryTestRule() // showShares is flaky - // @ScreenshotTest // todo run without real server + @Suppress("DEPRECATION") @Test fun showShares() { - Assert.assertTrue(ExistenceCheckRemoteOperation("/shareToAdmin/", true).execute(client).isSuccess) - Assert.assertTrue(CreateFolderRemoteOperation("/shareToAdmin/", true).execute(client).isSuccess) - Assert.assertTrue(CreateFolderRemoteOperation("/shareToGroup/", true).execute(client).isSuccess) - Assert.assertTrue(CreateFolderRemoteOperation("/shareViaLink/", true).execute(client).isSuccess) - Assert.assertTrue(CreateFolderRemoteOperation("/noShare/", true).execute(client).isSuccess) - // assertTrue(new CreateFolderRemoteOperation("/shareToCircle/", true).execute(client).isSuccess()); + assertTrue(ExistenceCheckRemoteOperation("/shareToAdmin/", true).execute(client).isSuccess) + assertTrue(CreateFolderRemoteOperation("/shareToAdmin/", true).execute(client).isSuccess) + assertTrue(CreateFolderRemoteOperation("/shareToGroup/", true).execute(client).isSuccess) + assertTrue(CreateFolderRemoteOperation("/shareViaLink/", true).execute(client).isSuccess) + assertTrue(CreateFolderRemoteOperation("/noShare/", true).execute(client).isSuccess) // share folder to user "admin" - Assert.assertTrue( + assertTrue( CreateShareRemoteOperation( "/shareToAdmin/", ShareType.USER, @@ -73,7 +79,7 @@ class FileDisplayActivityIT : AbstractOnServerIT() { ) // share folder via public link - Assert.assertTrue( + assertTrue( CreateShareRemoteOperation( "/shareViaLink/", ShareType.PUBLIC_LINK, @@ -85,7 +91,7 @@ class FileDisplayActivityIT : AbstractOnServerIT() { ) // share folder to group - Assert.assertTrue( + assertTrue( CreateShareRemoteOperation( "/shareToGroup/", ShareType.GROUP, @@ -96,101 +102,53 @@ class FileDisplayActivityIT : AbstractOnServerIT() { ).execute(client).isSuccess ) - // share folder to circle - // get share -// RemoteOperationResult searchResult = new GetShareesRemoteOperation("publicCircle", 1, 50).execute(client); -// assertTrue(searchResult.getLogMessage(), searchResult.isSuccess()); -// -// JSONObject resultJson = (JSONObject) searchResult.getData().get(0); -// String circleId = resultJson.getJSONObject("value").getString("shareWith"); -// -// assertTrue(new CreateShareRemoteOperation("/shareToCircle/", -// ShareType.CIRCLE, -// circleId, -// false, -// "", -// OCShare.DEFAULT_PERMISSION) -// .execute(client).isSuccess()); + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + // open drawer + onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) - val sut: Activity = activityRule.launchActivity(null) - InstrumentationRegistry.getInstrumentation().waitForIdleSync() - - // open drawer - onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) - - // click "shared" - onView(withId(R.id.nav_view)) - .perform(NavigationViewActions.navigateTo(R.id.nav_shared)) - shortSleep() - shortSleep() - // screenshot(sut) // todo run without real server + // click "shared" + onView(withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.nav_shared)) + } + } + } } + @Suppress("DEPRECATION") @Test fun allFiles() { - val sut = activityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + // given test folder + assertTrue( + CreateFolderOperation("/test/", user, targetContext, storageManager) + .execute(client) + .isSuccess + ) - // given test folder - Assert.assertTrue( - CreateFolderOperation("/test/", user, targetContext, storageManager) - .execute(client) - .isSuccess - ) + // navigate into it + val test = storageManager.getFileByPath("/test/") + sut.file = test + sut.startSyncFolderOperation(test, false) + assertEquals(storageManager.getFileByPath("/test/"), sut.currentDir) + EspressoIdlingResource.decrement() - // navigate into it - val test = storageManager.getFileByPath("/test/") - sut.file = test - sut.startSyncFolderOperation(test, false) - Assert.assertEquals(storageManager.getFileByPath("/test/"), sut.currentDir) + // open drawer + onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) - // open drawer - onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) + // click "all files" + onView(withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.nav_all_files)) - // click "all files" - onView(withId(R.id.nav_view)) - .perform(NavigationViewActions.navigateTo(R.id.nav_all_files)) - - // then should be in root again - shortSleep() - Assert.assertEquals(storageManager.getFileByPath("/"), sut.currentDir) - } - - @Test - fun checkToolbarTitleOnNavigation() { - // Create folder structure - val topFolder = "folder1" - val childFolder = "folder2" - - CreateFolderOperation("/$topFolder/", user, targetContext, storageManager) - .execute(client) - - CreateFolderOperation("/$topFolder/$childFolder/", user, targetContext, storageManager) - .execute(client) - - activityRule.launchActivity(null) - - shortSleep() - - // go into "foo" - onView(withText(topFolder)).perform(click()) - shortSleep() - - // check title is right - checkToolbarTitle(topFolder) - - // go into "bar" - onView(withText(childFolder)).perform(click()) - shortSleep() - - // check title is right - checkToolbarTitle(childFolder) - - // browse back up, we should be back in "foo" - Espresso.pressBack() - shortSleep() - - // check title is right - checkToolbarTitle(topFolder) + // then should be in root again + assertEquals(storageManager.getFileByPath("/"), sut.currentDir) + } + } + } } private fun checkToolbarTitle(childFolder: String) { @@ -203,8 +161,10 @@ class FileDisplayActivityIT : AbstractOnServerIT() { ) } + @Suppress("DEPRECATION") @Test fun browseFavoriteAndBack() { + EspressoIdlingResource.increment() // Create folder structure val topFolder = "folder1" @@ -212,52 +172,68 @@ class FileDisplayActivityIT : AbstractOnServerIT() { .execute(client) ToggleFavoriteRemoteOperation(true, "/$topFolder/") .execute(client) + EspressoIdlingResource.decrement() - val sut = activityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + // navigate to favorites + onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) + onView(withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.nav_favorites)) - // navigate to favorites - onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) - onView(withId(R.id.nav_view)) - .perform(NavigationViewActions.navigateTo(R.id.nav_favorites)) - shortSleep() + // check sort button is not shown, favorites are not sortable + onView( + withId(R.id.sort_button) + ).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))) - // check sort button is not shown, favorites are not sortable - onView(withId(R.id.sort_button)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))) + // browse into folder + onView(withId(R.id.list_root)) + .perform(closeSoftKeyboard()) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + 0, + click() + ) + ) + checkToolbarTitle(topFolder) + // sort button should now be visible + onView(withId(R.id.sort_button)).check(matches(ViewMatchers.isDisplayed())) - // browse into folder - onView(withId(R.id.list_root)) - .perform(closeSoftKeyboard()) - .perform( - RecyclerViewActions.actionOnItemAtPosition( - 0, - click() - ) - ) - shortSleep() - checkToolbarTitle(topFolder) - // sort button should now be visible - onView(withId(R.id.sort_button)).check(matches(ViewMatchers.isDisplayed())) - - // browse back, should be back to All Files - Espresso.pressBack() - checkToolbarTitle(sut.getString(R.string.app_name)) - onView(withId(R.id.sort_button)).check(matches(ViewMatchers.isDisplayed())) + // browse back, should be back to All Files + Espresso.pressBack() + checkToolbarTitle(sut.getString(R.string.app_name)) + onView(withId(R.id.sort_button)).check(matches(ViewMatchers.isDisplayed())) + } + } + } } + @Suppress("DEPRECATION") @Test fun switchToGridView() { - activityRule.launchActivity(null) - Assert.assertTrue( - CreateFolderOperation("/test/", user, targetContext, storageManager) - .execute(client) - .isSuccess - ) - onView(withId(R.id.switch_grid_view_button)).perform(click()) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + assertTrue( + CreateFolderOperation("/test/", user, targetContext, storageManager) + .execute(client) + .isSuccess + ) + onView(withId(R.id.switch_grid_view_button)).perform(click()) + } + } + } } @Test fun openAccountSwitcher() { - activityRule.launchActivity(null) - onView(withId(R.id.switch_account_button)).perform(click()) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + onView(withId(R.id.switch_account_button)).perform(click()) + } + } + } } } diff --git a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt index caa0e47..2b7c1e8 100644 --- a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt @@ -1,35 +1,50 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client import android.Manifest -import androidx.test.espresso.Espresso +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.DrawerActions import androidx.test.espresso.contrib.NavigationViewActions -import androidx.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.rule.GrantPermissionRule import com.owncloud.android.AbstractIT import com.owncloud.android.R import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.fragment.EmptyListState +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest +import org.junit.After import org.junit.Assert +import org.junit.Before import org.junit.Rule import org.junit.Test class FileDisplayActivityScreenshotIT : AbstractIT() { - @get:Rule - val activityRule = IntentsTestRule( - FileDisplayActivity::class.java, - true, - false - ) + private val testClassName = "com.nextcloud.client.FileDisplayActivityScreenshotIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @get:Rule val permissionRule: GrantPermissionRule = GrantPermissionRule.grant( @@ -41,82 +56,110 @@ class FileDisplayActivityScreenshotIT : AbstractIT() { } @Test + @UiThread @ScreenshotTest fun open() { try { - val sut = activityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - shortSleep() - sut.runOnUiThread { - sut.listOfFilesFragment!!.setFabEnabled(false) - sut.resetScrolling(true) - sut.listOfFilesFragment!!.setEmptyListLoadingMessage() - sut.listOfFilesFragment!!.isLoading = false + sut.run { + listOfFilesFragment?.let { + it.setFabEnabled(false) + resetScrolling(true) + it.setEmptyListMessage(EmptyListState.LOADING) + it.isLoading = false + } + } + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - shortSleep() - waitForIdleSync() - screenshot(sut) } catch (e: SecurityException) { Log_OC.e(TAG, "Error caught at open $e") } } @Test + @UiThread @ScreenshotTest fun showMediaThenAllFiles() { try { - val fileDisplayActivity = activityRule.launchActivity(null) - val sut = fileDisplayActivity.listOfFilesFragment - Assert.assertNotNull(sut) - sut!!.setFabEnabled(false) - sut.setEmptyListLoadingMessage() - sut.isLoading = false + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val fragment = sut.listOfFilesFragment + Assert.assertNotNull(fragment) + fragment!!.setFabEnabled(false) + fragment.setEmptyListMessage(EmptyListState.LOADING) + fragment.isLoading = false + EspressoIdlingResource.decrement() - // open drawer - Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open()) + onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open()) - // click "all files" - Espresso.onView(ViewMatchers.withId(R.id.nav_view)) - .perform(NavigationViewActions.navigateTo(R.id.nav_gallery)) + onView(ViewMatchers.withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.nav_gallery)) - // wait - shortSleep() + onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open()) + onView(ViewMatchers.withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.nav_all_files)) - // click "all files" - Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open()) - Espresso.onView(ViewMatchers.withId(R.id.nav_view)) - .perform(NavigationViewActions.navigateTo(R.id.nav_all_files)) + EspressoIdlingResource.increment() + fragment.setFabEnabled(false) + fragment.setEmptyListMessage(EmptyListState.LOADING) + fragment.isLoading = false + EspressoIdlingResource.decrement() - // then compare screenshot - shortSleep() - sut.setFabEnabled(false) - sut.setEmptyListLoadingMessage() - sut.isLoading = false - shortSleep() - screenshot(fileDisplayActivity) + val screenShotName = createName(testClassName + "_" + "showMediaThenAllFiles", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } catch (e: SecurityException) { Log_OC.e(TAG, "Error caught at open $e") } } @Test + @UiThread @ScreenshotTest fun drawer() { try { - val sut = activityRule.launchActivity(null) - Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open()) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open()) - shortSleep() - sut.runOnUiThread { - sut.hideInfoBox() - sut.resetScrolling(true) - sut.listOfFilesFragment!!.setFabEnabled(false) - sut.listOfFilesFragment!!.setEmptyListLoadingMessage() - sut.listOfFilesFragment!!.isLoading = false + EspressoIdlingResource.increment() + + sut.run { + hideInfoBox() + resetScrolling(true) + + listOfFilesFragment?.let { + it.setFabEnabled(false) + it.setEmptyListMessage(EmptyListState.LOADING) + it.isLoading = false + } + } + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "drawer", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - shortSleep() - waitForIdleSync() - screenshot(sut) } catch (e: SecurityException) { Log_OC.e(TAG, "Error caught at open $e") } diff --git a/app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.java deleted file mode 100644 index b025a25..0000000 --- a/app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.nextcloud.client; - -import android.app.Activity; - -import com.nextcloud.client.onboarding.FirstRunActivity; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - -public class FirstRunActivityIT extends AbstractIT { - @Rule public IntentsTestRule activityRule = new IntentsTestRule<>(FirstRunActivity.class, - true, - false); - - @Test - @ScreenshotTest - public void open() { - Activity sut = activityRule.launchActivity(null); - - screenshot(sut); - } - -} diff --git a/app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.kt new file mode 100644 index 0000000..23df993 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/client/FirstRunActivityIT.kt @@ -0,0 +1,53 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2019 Tobias Kaminsky + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.client + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.client.onboarding.FirstRunActivity +import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Test + +class FirstRunActivityIT : AbstractIT() { + private val testClassName = "com.nextcloud.client.FirstRunActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + @ScreenshotTest + fun open() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/nextcloud/client/SettingsActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/SettingsActivityIT.kt index dc8878f..193c210 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SettingsActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/SettingsActivityIT.kt @@ -1,71 +1,109 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client -import android.app.Activity import android.content.Intent import android.os.Looper -import androidx.test.espresso.intent.rule.IntentsTestRule -import com.nextcloud.test.GrantStoragePermissionRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.test.GrantStoragePermissionRule.Companion.grant import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.ui.activity.RequestCredentialsActivity import com.owncloud.android.ui.activity.SettingsActivity import com.owncloud.android.utils.EncryptionUtils +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest +import org.junit.After import org.junit.Assert +import org.junit.Before import org.junit.Rule import org.junit.Test +import org.junit.rules.TestRule @Suppress("FunctionNaming") class SettingsActivityIT : AbstractIT() { - @get:Rule - val activityRule = IntentsTestRule( - SettingsActivity::class.java, - true, - false - ) + private val testClassName = "com.nextcloud.client.SettingsActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @get:Rule - val permissionRule = GrantStoragePermissionRule.grant() + var storagePermissionRule: TestRule = grant() @Test + @UiThread @ScreenshotTest fun open() { - val sut: Activity = activityRule.launchActivity(null) - screenshot(sut) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun showMnemonic_Error() { - val sut = activityRule.launchActivity(null) - sut.handleMnemonicRequest(null) - shortSleep() - waitForIdleSync() - screenshot(sut) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + sut.handleMnemonicRequest(null) + val screenShotName = createName(testClassName + "_" + "showMnemonic_Error", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } + @Suppress("DEPRECATION") @Test + @UiThread fun showMnemonic() { if (Looper.myLooper() == null) { Looper.prepare() } - val intent = Intent() - intent.putExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) - val arbitraryDataProvider = ArbitraryDataProviderImpl(targetContext) - arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, EncryptionUtils.MNEMONIC, "Secret mnemonic") - val sut = activityRule.launchActivity(null) - sut.runOnUiThread { - sut.handleMnemonicRequest(intent) + val intent = Intent().apply { + putExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) } - Looper.myLooper()?.quitSafely() - Assert.assertTrue(true) // if we reach this, everything is ok + ArbitraryDataProviderImpl(targetContext).run { + storeOrUpdateKeyValue(user.accountName, EncryptionUtils.MNEMONIC, "Secret mnemonic") + } + + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + sut.handleMnemonicRequest(intent) + onView(isRoot()).check(matches(isDisplayed())) + Looper.myLooper()?.quitSafely() + Assert.assertTrue(true) + } + } + } } } diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java deleted file mode 100644 index ba43a43..0000000 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.nextcloud.client; - -import android.app.Activity; -import android.content.Intent; - -import com.nextcloud.client.preferences.SubFolderRule; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.databinding.SyncedFoldersLayoutBinding; -import com.owncloud.android.datamodel.MediaFolderType; -import com.owncloud.android.datamodel.SyncedFolder; -import com.owncloud.android.datamodel.SyncedFolderDisplayItem; -import com.owncloud.android.ui.activity.SyncedFoldersActivity; -import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; - -import java.util.Objects; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - - -public class SyncedFoldersActivityIT extends AbstractIT { - @Rule public IntentsTestRule activityRule = new IntentsTestRule<>(SyncedFoldersActivity.class, - true, - false); - - @Test - @ScreenshotTest - public void open() { - SyncedFoldersActivity activity = activityRule.launchActivity(null); - activity.adapter.clear(); - SyncedFoldersLayoutBinding sut = activity.binding; - shortSleep(); - screenshot(sut.emptyList.emptyListView); - } - - @Test - @ScreenshotTest - public void testSyncedFolderDialog() { - SyncedFolderDisplayItem item = new SyncedFolderDisplayItem(1, - "/sdcard/DCIM/", - "/InstantUpload/", - true, - false, - false, - true, - "test@https://nextcloud.localhost", - 0, - 0, - true, - 1000, - "Name", - MediaFolderType.IMAGE, - false, - SubFolderRule.YEAR_MONTH, - false, - SyncedFolder.NOT_SCANNED_YET); - SyncedFolderPreferencesDialogFragment sut = SyncedFolderPreferencesDialogFragment.newInstance(item, 0); - - Intent intent = new Intent(targetContext, SyncedFoldersActivity.class); - SyncedFoldersActivity activity = activityRule.launchActivity(intent); - - sut.show(activity.getSupportFragmentManager(), ""); - - getInstrumentation().waitForIdleSync(); - shortSleep(); - - screenshot(Objects.requireNonNull(sut.requireDialog().getWindow()).getDecorView()); - } -} diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt new file mode 100644 index 0000000..f095f1a --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.kt @@ -0,0 +1,132 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.client + +import android.content.Intent +import android.os.Looper +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.client.preferences.SubFolderRule +import com.owncloud.android.AbstractIT +import com.owncloud.android.datamodel.MediaFolderType +import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.datamodel.SyncedFolderDisplayItem +import com.owncloud.android.ui.activity.SyncedFoldersActivity +import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment.Companion.newInstance +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Test + +class SyncedFoldersActivityIT : AbstractIT() { + private val testClassName = "com.nextcloud.client.SyncedFoldersActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + @ScreenshotTest + fun open() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.adapter.clear() + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.binding.emptyList.emptyListView, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun testSyncedFolderDialog() { + val item = SyncedFolderDisplayItem( + 1, + "/sdcard/DCIM/", + "/InstantUpload/", + true, + false, + false, + true, + "test@https://nextcloud.localhost", + 0, + 0, + true, + 1000, + "Name", + MediaFolderType.IMAGE, + false, + SubFolderRule.YEAR_MONTH, + false, + SyncedFolder.NOT_SCANNED_YET + ) + val fragment = newInstance(item, 0) + + val intent = Intent(targetContext, SyncedFoldersActivity::class.java) + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + fragment?.show(sut.supportFragmentManager, "") + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "testSyncedFolderDialog", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshot(fragment?.requireDialog()?.window?.decorView, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun showPowerCheckDialog() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + val intent = Intent(targetContext, SyncedFoldersActivity::class.java) + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val dialog = sut.buildPowerCheckDialog() + dialog.show() + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showPowerCheckDialog", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshot(dialog.window?.decorView, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/nextcloud/client/TestRunner.kt b/app/src/androidTest/java/com/nextcloud/client/TestRunner.kt index 37beeb5..d17ebcd 100644 --- a/app/src/androidTest/java/com/nextcloud/client/TestRunner.kt +++ b/app/src/androidTest/java/com/nextcloud/client/TestRunner.kt @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client diff --git a/app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.java deleted file mode 100644 index c8415b0..0000000 --- a/app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.nextcloud.client; - -import com.owncloud.android.AbstractIT; -import com.owncloud.android.ui.activity.UploadListActivity; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - - -public class UploadListActivityActivityIT extends AbstractIT { - @Rule public IntentsTestRule activityRule = new IntentsTestRule<>(UploadListActivity.class, - true, - false); - - @Test - @ScreenshotTest - public void openDrawer() { - super.openDrawer(activityRule); - } -} diff --git a/app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.kt new file mode 100644 index 0000000..a01cdd5 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/client/UploadListActivityActivityIT.kt @@ -0,0 +1,58 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.client + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.DrawerActions +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.matcher.ViewMatchers.withId +import com.owncloud.android.AbstractIT +import com.owncloud.android.R +import com.owncloud.android.ui.activity.UploadListActivity +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Test + +class UploadListActivityActivityIT : AbstractIT() { + private val testClassName = "com.nextcloud.client.UploadListActivityActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + @ScreenshotTest + fun openDrawer() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + onView(isRoot()).check(matches(isDisplayed())) + onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) + + val screenShotName = createName(testClassName + "_" + "openDrawer", "") + screenshotViaName(sut, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/nextcloud/client/account/AnonymousUserTest.kt b/app/src/androidTest/java/com/nextcloud/client/account/AnonymousUserTest.kt index 23da22a..7def852 100644 --- a/app/src/androidTest/java/com/nextcloud/client/account/AnonymousUserTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/account/AnonymousUserTest.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account diff --git a/app/src/androidTest/java/com/nextcloud/client/account/MockUserTest.kt b/app/src/androidTest/java/com/nextcloud/client/account/MockUserTest.kt index e3b6f67..f45afff 100644 --- a/app/src/androidTest/java/com/nextcloud/client/account/MockUserTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/account/MockUserTest.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account diff --git a/app/src/androidTest/java/com/nextcloud/client/account/OwnCloudClientManagerTest.java b/app/src/androidTest/java/com/nextcloud/client/account/OwnCloudClientManagerTest.java index 61f2dc3..1c832c1 100644 --- a/app/src/androidTest/java/com/nextcloud/client/account/OwnCloudClientManagerTest.java +++ b/app/src/androidTest/java/com/nextcloud/client/account/OwnCloudClientManagerTest.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account; diff --git a/app/src/androidTest/java/com/nextcloud/client/account/RegisteredUserTest.kt b/app/src/androidTest/java/com/nextcloud/client/account/RegisteredUserTest.kt index 52b517a..d429c4a 100644 --- a/app/src/androidTest/java/com/nextcloud/client/account/RegisteredUserTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/account/RegisteredUserTest.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account @@ -12,7 +12,7 @@ import android.net.Uri import android.os.Parcel import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.lib.common.OwnCloudBasicCredentials -import com.owncloud.android.lib.resources.status.OwnCloudVersion +import com.owncloud.android.lib.resources.status.NextcloudVersion import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotSame @@ -31,7 +31,7 @@ class RegisteredUserTest { val ownCloudAccount = OwnCloudAccount(uri, credentials) val server = Server( uri = URI(uri.toString()), - version = OwnCloudVersion.nextcloud_17 + version = NextcloudVersion.nextcloud_31 ) return RegisteredUser( account = account, diff --git a/app/src/androidTest/java/com/nextcloud/client/account/UserAccountManagerImplTest.java b/app/src/androidTest/java/com/nextcloud/client/account/UserAccountManagerImplTest.java index d24aae4..053759d 100644 --- a/app/src/androidTest/java/com/nextcloud/client/account/UserAccountManagerImplTest.java +++ b/app/src/androidTest/java/com/nextcloud/client/account/UserAccountManagerImplTest.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019-2023 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account; diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index 20cf85b..e7cb55f 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -1,14 +1,15 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant import com.nextcloud.client.assistant.repository.AssistantRepository import com.owncloud.android.AbstractOnServerIT +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData import com.owncloud.android.lib.resources.status.NextcloudVersion import org.junit.Assert.assertTrue import org.junit.Before @@ -21,7 +22,7 @@ class AssistantRepositoryTests : AbstractOnServerIT() { @Before fun setup() { - sut = AssistantRepository(nextcloudClient) + sut = AssistantRepository(nextcloudClient, capability) } @Test @@ -33,10 +34,7 @@ class AssistantRepositoryTests : AbstractOnServerIT() { } val result = sut?.getTaskTypes() - assertTrue(result?.isSuccess == true) - - val taskTypes = result?.resultData?.types - assertTrue(taskTypes?.isNotEmpty() == true) + assertTrue(result?.isNotEmpty() == true) } @Test @@ -48,10 +46,7 @@ class AssistantRepositoryTests : AbstractOnServerIT() { } val result = sut?.getTaskList("assistant") - assertTrue(result?.isSuccess == true) - - val taskList = result?.resultData?.tasks - assertTrue(taskList?.isEmpty() == true || (taskList?.size ?: 0) > 0) + assertTrue(result?.isEmpty() == true || (result?.size ?: 0) > 0) } @Test @@ -63,8 +58,14 @@ class AssistantRepositoryTests : AbstractOnServerIT() { } val input = "Give me some random output for test purpose" - val type = "OCP\\TextProcessing\\FreePromptTaskType" - val result = sut?.createTask(input, type) + val taskType = TaskTypeData( + "core:text2text", + "Free text to text prompt", + "Runs an arbitrary prompt through a language model that returns a reply", + emptyMap(), + emptyMap() + ) + val result = sut?.createTask(input, taskType) assertTrue(result?.isSuccess == true) } @@ -80,13 +81,11 @@ class AssistantRepositoryTests : AbstractOnServerIT() { sleep(120) - val resultOfTaskList = sut?.getTaskList("assistant") - assertTrue(resultOfTaskList?.isSuccess == true) + val taskList = sut?.getTaskList("assistant") + assertTrue(taskList != null) sleep(120) - val taskList = resultOfTaskList?.resultData?.tasks - assert((taskList?.size ?: 0) > 0) val result = sut?.deleteTask(taskList!!.first().id) diff --git a/app/src/androidTest/java/com/nextcloud/client/database/migrations/MigrationTest.kt b/app/src/androidTest/java/com/nextcloud/client/database/migrations/MigrationTest.kt index af0d3ab..00e96dc 100644 --- a/app/src/androidTest/java/com/nextcloud/client/database/migrations/MigrationTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/database/migrations/MigrationTest.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.database.migrations diff --git a/app/src/androidTest/java/com/nextcloud/client/documentscan/GeneratePDFUseCaseTest.kt b/app/src/androidTest/java/com/nextcloud/client/documentscan/GeneratePDFUseCaseTest.kt index d5f1c8f..36402fb 100644 --- a/app/src/androidTest/java/com/nextcloud/client/documentscan/GeneratePDFUseCaseTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/documentscan/GeneratePDFUseCaseTest.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.documentscan diff --git a/app/src/androidTest/java/com/nextcloud/client/etm/EtmActivityTest.kt b/app/src/androidTest/java/com/nextcloud/client/etm/EtmActivityTest.kt index 3b23cad..7297dfc 100644 --- a/app/src/androidTest/java/com/nextcloud/client/etm/EtmActivityTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/etm/EtmActivityTest.kt @@ -3,39 +3,68 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.etm -import android.app.Activity -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class EtmActivityTest : AbstractIT() { - @get:Rule - var activityRule = IntentsTestRule(EtmActivity::class.java, true, false) + private val testClassName = "com.nextcloud.client.etm.EtmActivityTest" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun overview() { - val sut: Activity = activityRule.launchActivity(null) - - waitForIdleSync() - - screenshot(sut) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "overview", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun accounts() { - val sut: EtmActivity = activityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.vm.onPageSelected(1) + EspressoIdlingResource.decrement() - UiThreadStatement.runOnUiThread { sut.vm.onPageSelected(1) } - - screenshot(sut) + val screenShotName = createName(testClassName + "_" + "accounts", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } } diff --git a/app/src/androidTest/java/com/nextcloud/client/files/DeepLinkHandlerTest.kt b/app/src/androidTest/java/com/nextcloud/client/files/DeepLinkHandlerTest.kt index 1b86f91..f6a7115 100644 --- a/app/src/androidTest/java/com/nextcloud/client/files/DeepLinkHandlerTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/files/DeepLinkHandlerTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files @@ -37,8 +37,8 @@ class DeepLinkHandlerTest { class DeepLinkPattern { companion object { - val FILE_ID = 1234 - val SERVER_BASE_URLS = listOf( + private const val FILE_ID = 1234 + private val SERVER_BASE_URLS = listOf( "http://hostname.net", "https://hostname.net", "http://hostname.net/subdir1", @@ -48,7 +48,7 @@ class DeepLinkHandlerTest { "http://hostname.net/subdir1/subdir2/subdir3", "https://hostname.net/subdir1/subdir2/subdir3" ) - val INDEX_PHP_PATH = listOf( + private val INDEX_PHP_PATH = listOf( "", "/index.php" ) @@ -102,12 +102,12 @@ class DeepLinkHandlerTest { const val OTHER_SERVER_BASE_URL = "https://someotherserver.net" const val SERVER_BASE_URL = "https://server.net" const val FILE_ID = "1234567890" - val DEEP_LINK = Uri.parse("$SERVER_BASE_URL/index.php/f/$FILE_ID") + val DEEP_LINK: Uri = Uri.parse("$SERVER_BASE_URL/index.php/f/$FILE_ID") fun createMockUser(serverBaseUrl: String): User { val user = mock() val uri = URI.create(serverBaseUrl) - val server = Server(uri = uri, version = OwnCloudVersion.nextcloud_19) + val server = Server(uri = uri, version = OwnCloudVersion.nextcloud_20) whenever(user.server).thenReturn(server) return user } @@ -115,8 +115,8 @@ class DeepLinkHandlerTest { @Mock lateinit var userAccountManager: UserAccountManager - lateinit var allUsers: List - lateinit var handler: DeepLinkHandler + private lateinit var allUsers: List + private lateinit var handler: DeepLinkHandler @Before fun setUp() { diff --git a/app/src/androidTest/java/com/nextcloud/client/files/download/DownloaderServiceTest.kt b/app/src/androidTest/java/com/nextcloud/client/files/download/DownloaderServiceTest.kt index 14b8e42..d343487 100644 --- a/app/src/androidTest/java/com/nextcloud/client/files/download/DownloaderServiceTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/files/download/DownloaderServiceTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files.download diff --git a/app/src/androidTest/java/com/nextcloud/client/files/download/RegistryTest.kt b/app/src/androidTest/java/com/nextcloud/client/files/download/RegistryTest.kt index bf49bf4..2bf7517 100644 --- a/app/src/androidTest/java/com/nextcloud/client/files/download/RegistryTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/files/download/RegistryTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files.download diff --git a/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerConnectionTest.kt b/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerConnectionTest.kt index 516b332..5a9d1bf 100644 --- a/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerConnectionTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerConnectionTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files.download diff --git a/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerTest.kt b/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerTest.kt index 692b268..e5983fe 100644 --- a/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/files/download/TransferManagerTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.files.download diff --git a/app/src/androidTest/java/com/nextcloud/client/integrations/deck/DeckApiTest.kt b/app/src/androidTest/java/com/nextcloud/client/integrations/deck/DeckApiTest.kt index 784d6de..46abfc0 100644 --- a/app/src/androidTest/java/com/nextcloud/client/integrations/deck/DeckApiTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/integrations/deck/DeckApiTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.integrations.deck @@ -65,9 +65,7 @@ class DeckApiTest { companion object { @Parameterized.Parameters @JvmStatic - fun initParametrs(): Array { - return DeckApiImpl.DECK_APP_PACKAGES - } + fun initParametrs(): Array = DeckApiImpl.DECK_APP_PACKAGES } @Before diff --git a/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt b/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt index e90b81c..d59256f 100644 --- a/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt @@ -2,10 +2,11 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs +import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer @@ -19,13 +20,19 @@ import androidx.work.WorkInfo import androidx.work.WorkManager import com.nextcloud.client.account.User import com.nextcloud.client.core.Clock +import com.nextcloud.utils.extensions.toByteArray +import com.owncloud.android.lib.common.utils.Log_OC +import org.apache.commons.io.FileUtils import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue +import org.junit.Assert.fail import org.junit.Before +import org.junit.Rule import org.junit.Test +import org.junit.rules.TemporaryFolder import org.junit.runner.RunWith import org.junit.runners.Suite import org.mockito.ArgumentMatcher @@ -37,6 +44,8 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.io.File +import java.io.IOException import java.util.Date import java.util.UUID import java.util.concurrent.CountDownLatch @@ -82,9 +91,11 @@ class BackgroundJobManagerTest { internal lateinit var workManager: WorkManager internal lateinit var clock: Clock internal lateinit var backgroundJobManager: BackgroundJobManagerImpl + internal lateinit var context: Context @Before fun setUpFixture() { + context = mock() user = mock() whenever(user.accountName).thenReturn(USER_ACCOUNT_NAME) workManager = mock() @@ -302,16 +313,36 @@ class BackgroundJobManagerTest { private lateinit var jobInfo: LiveData private lateinit var request: OneTimeWorkRequest + @get:Rule + var folder: TemporaryFolder = TemporaryFolder() + @Before fun setUp() { + var selectedContactsFile: File? = null + try { + selectedContactsFile = folder.newFile("hashset_cache.txt") + } catch (_: IOException) { + Log_OC.e("ImmediateContactsImport", "error creating temporary test file in ") + fail("hashset_cache cannot be found") + } + + if (selectedContactsFile == null) { + fail("hashset_cache cannot be found") + } + val requestCaptor: KArgumentCaptor = argumentCaptor() workInfo = MutableLiveData() whenever(workManager.getWorkInfoByIdLiveData(any())).thenReturn(workInfo) + + val selectedContacts = intArrayOf(1, 2, 3) + val contractsAsByteArray = selectedContacts.toByteArray() + FileUtils.writeByteArrayToFile(selectedContactsFile, contractsAsByteArray) + jobInfo = backgroundJobManager.startImmediateContactsImport( contactsAccountName = "name", contactsAccountType = "type", vCardFilePath = "/path/to/vcard/file", - selectedContacts = intArrayOf(1, 2, 3) + selectedContactsFilePath = selectedContactsFile!!.absolutePath ) verify(workManager).enqueueUniqueWork( any(), diff --git a/app/src/androidTest/java/com/nextcloud/client/jobs/ContactsBackupIT.kt b/app/src/androidTest/java/com/nextcloud/client/jobs/ContactsBackupIT.kt index 0a5dec7..bb68dfc 100644 --- a/app/src/androidTest/java/com/nextcloud/client/jobs/ContactsBackupIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/jobs/ContactsBackupIT.kt @@ -1,8 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.nextcloud.client.jobs @@ -11,73 +10,120 @@ import android.Manifest import androidx.test.rule.GrantPermissionRule import androidx.work.WorkManager import com.nextcloud.client.core.ClockImpl +import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.client.preferences.AppPreferencesImpl import com.nextcloud.test.RetryTestRule -import com.owncloud.android.AbstractIT +import com.nextcloud.utils.extensions.toByteArray import com.owncloud.android.AbstractOnServerIT import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.DownloadFileOperation import ezvcard.Ezvcard import ezvcard.VCard -import junit.framework.Assert.assertEquals -import junit.framework.Assert.assertTrue +import org.apache.commons.io.FileUtils +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Before import org.junit.Rule import org.junit.Test +import org.junit.rules.TemporaryFolder import java.io.BufferedInputStream import java.io.File import java.io.FileInputStream +import java.io.IOException class ContactsBackupIT : AbstractOnServerIT() { - val workmanager = WorkManager.getInstance(targetContext) - val preferences = AppPreferencesImpl.fromContext(targetContext) - private val backgroundJobManager = BackgroundJobManagerImpl(workmanager, ClockImpl(), preferences) + private val workManager = WorkManager.getInstance(targetContext) + private val preferences: AppPreferences = AppPreferencesImpl.fromContext(targetContext) + private val backgroundJobManager = BackgroundJobManagerImpl(workManager, ClockImpl(), preferences) @get:Rule - val writeContactsRule = GrantPermissionRule.grant(Manifest.permission.WRITE_CONTACTS) + val writeContactsRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_CONTACTS) @get:Rule - val readContactsRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS) + val readContactsRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS) @get:Rule val retryTestRule = RetryTestRule() // flaky test + @get:Rule + var folder: TemporaryFolder = TemporaryFolder() + private val vcard: String = "vcard.vcf" + private var selectedContactsFile: File? = null + + @Before + fun setup() { + try { + selectedContactsFile = folder.newFile("hashset_cache.txt") + } catch (_: IOException) { + Log_OC.e("ContactsBackupIT", "error creating temporary test file in ") + } + } @Test fun importExport() { - val intArray = IntArray(1) - intArray[0] = 0 + val intArray = intArrayOf(0) + if (selectedContactsFile == null) { + fail("hashset_cache cannot be found") + } + + val contractsAsByteArray = intArray.toByteArray() + FileUtils.writeByteArrayToFile(selectedContactsFile, contractsAsByteArray) // import file to local contacts - backgroundJobManager.startImmediateContactsImport(null, null, getFile(vcard).absolutePath, intArray) - - shortSleep() + backgroundJobManager.startImmediateContactsImport( + null, + null, + getFile(vcard).absolutePath, + selectedContactsFile!!.absolutePath + ) + longSleep() // export contact backgroundJobManager.startImmediateContactsBackup(user) - longSleep() - val backupFolder: String = targetContext.resources.getString(R.string.contacts_backup_folder) + + val folderPath: String = targetContext.resources.getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR refreshFolder("/") longSleep() - - refreshFolder(backupFolder) longSleep() - val backupOCFile = storageManager.getFolderContent( - storageManager.getFileByDecryptedRemotePath(backupFolder), - false - )[0] + refreshFolder(folderPath) + longSleep() + longSleep() - assertTrue(DownloadFileOperation(user, backupOCFile, AbstractIT.targetContext).execute(client).isSuccess) + if (folderPath.isEmpty()) { + fail("folderPath cannot be empty") + } + + val folder = fileDataStorageManager.getFileByDecryptedRemotePath(folderPath) + if (folder == null) { + fail("folder cannot be null") + } + + val ocFile = storageManager.getFolderContent(folder, false).firstOrNull() + if (ocFile == null) { + fail("ocFile cannot be null") + } + + if (ocFile?.storagePath == null) { + fail("ocFile.storagePath cannot be null") + } + + assertTrue(DownloadFileOperation(user, ocFile, targetContext).execute(client).isSuccess) + + val file = ocFile?.storagePath?.let { File(it) } + if (file == null) { + fail("file cannot be null") + } - val backupFile = File(backupOCFile.storagePath) val vcardInputStream = BufferedInputStream(FileInputStream(getFile(vcard))) - val backupFileInputStream = BufferedInputStream(FileInputStream(backupFile)) + val backupFileInputStream = BufferedInputStream(FileInputStream(file)) // verify same val originalCards: ArrayList = ArrayList() @@ -87,6 +133,17 @@ class ContactsBackupIT : AbstractOnServerIT() { backupCards.addAll(Ezvcard.parse(backupFileInputStream).all()) assertEquals(originalCards.size, backupCards.size) - assertEquals(originalCards[0].formattedName.toString(), backupCards[0].formattedName.toString()) + + val originalCardFormattedName = originalCards.firstOrNull()?.formattedName + if (originalCardFormattedName == null) { + fail("originalCardFormattedName cannot be null") + } + + val backupCardFormattedName = backupCards.firstOrNull()?.formattedName + if (backupCardFormattedName == null) { + fail("backupCardFormattedName cannot be null") + } + + assertEquals(originalCardFormattedName.toString(), backupCardFormattedName.toString()) } } diff --git a/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsDbTest.kt b/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsDbTest.kt index 92602e2..035ec3d 100644 --- a/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsDbTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsDbTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations diff --git a/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsManagerTest.kt b/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsManagerTest.kt index af02855..0d12bda 100644 --- a/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsManagerTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/migrations/MigrationsManagerTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations diff --git a/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferences.kt b/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferences.kt index 390f7ff..bd87169 100644 --- a/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferences.kt +++ b/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferences.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations @@ -69,25 +69,16 @@ class MockSharedPreferences : SharedPreferences { override fun getInt(key: String?, defValue: Int): Int = store.getOrDefault(key, defValue) as Int - override fun getAll(): MutableMap { - return HashMap(store) - } + override fun getAll(): MutableMap = HashMap(store) - override fun edit(): SharedPreferences.Editor { - return MockEditor(store) - } + override fun edit(): SharedPreferences.Editor = MockEditor(store) - override fun getLong(key: String?, defValue: Long): Long { - throw UnsupportedOperationException() - } + override fun getLong(key: String?, defValue: Long): Long = throw UnsupportedOperationException() - override fun getFloat(key: String?, defValue: Float): Float { - throw UnsupportedOperationException() - } + override fun getFloat(key: String?, defValue: Float): Float = throw UnsupportedOperationException() - override fun getStringSet(key: String?, defValues: MutableSet?): MutableSet? { - return store.getOrDefault(key, defValues) as MutableSet? - } + override fun getStringSet(key: String?, defValues: MutableSet?): MutableSet? = + store.getOrDefault(key, defValues) as MutableSet? override fun registerOnSharedPreferenceChangeListener( listener: SharedPreferences.OnSharedPreferenceChangeListener? diff --git a/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferencesTest.kt b/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferencesTest.kt index 8ea5830..6654483 100644 --- a/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferencesTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/migrations/MockSharedPreferencesTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.migrations diff --git a/app/src/androidTest/java/com/nextcloud/client/network/ConnectivityServiceImplIT.kt b/app/src/androidTest/java/com/nextcloud/client/network/ConnectivityServiceImplIT.kt index 4935106..97ce829 100644 --- a/app/src/androidTest/java/com/nextcloud/client/network/ConnectivityServiceImplIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/network/ConnectivityServiceImplIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.network diff --git a/app/src/androidTest/java/com/nextcloud/extensions/BitmapRotationTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/BitmapRotationTests.kt new file mode 100644 index 0000000..a2394f4 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/extensions/BitmapRotationTests.kt @@ -0,0 +1,90 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.extensions + +import android.graphics.Bitmap +import android.graphics.Color +import androidx.exifinterface.media.ExifInterface +import com.nextcloud.utils.rotateBitmapViaExif +import junit.framework.TestCase.assertEquals +import org.junit.Test + +class BitmapRotationTests { + + private fun createTestBitmap(): Bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888).apply { + setPixel(0, 0, Color.RED) + setPixel(1, 0, Color.GREEN) + setPixel(0, 1, Color.BLUE) + setPixel(1, 1, Color.YELLOW) + } + + @Test + fun testRotateBitmapViaExifWhenGivenNullBitmapShouldReturnNull() { + val rotated = null.rotateBitmapViaExif(ExifInterface.ORIENTATION_ROTATE_90) + assertEquals(null, rotated) + } + + @Test + fun testRotateBitmapViaExifWhenGivenNormalOrientationShouldReturnSameBitmap() { + val bmp = createTestBitmap() + val rotated = bmp.rotateBitmapViaExif(ExifInterface.ORIENTATION_NORMAL) + assertEquals(bmp, rotated) + } + + @Test + fun testRotateBitmapViaExifWhenGivenRotate90ShouldReturnRotatedBitmap() { + val bmp = createTestBitmap() + val rotated = bmp.rotateBitmapViaExif(ExifInterface.ORIENTATION_ROTATE_90)!! + assertEquals(bmp.width, rotated.height) + assertEquals(bmp.height, rotated.width) + + assertEquals(Color.BLUE, rotated.getPixel(0, 0)) + assertEquals(Color.RED, rotated.getPixel(1, 0)) + assertEquals(Color.YELLOW, rotated.getPixel(0, 1)) + assertEquals(Color.GREEN, rotated.getPixel(1, 1)) + } + + @Test + fun testRotateBitmapViaExifWhenGivenRotate180ShouldReturnRotatedBitmap() { + val bmp = createTestBitmap() + val rotated = bmp.rotateBitmapViaExif(ExifInterface.ORIENTATION_ROTATE_180)!! + assertEquals(bmp.width, rotated.width) + assertEquals(bmp.height, rotated.height) + + assertEquals(Color.YELLOW, rotated.getPixel(0, 0)) + assertEquals(Color.BLUE, rotated.getPixel(1, 0)) + assertEquals(Color.GREEN, rotated.getPixel(0, 1)) + assertEquals(Color.RED, rotated.getPixel(1, 1)) + } + + @Test + fun testRotateBitmapViaExifWhenGivenFlipHorizontalShouldReturnFlippedBitmap() { + val bmp = createTestBitmap() + val rotated = bmp.rotateBitmapViaExif(ExifInterface.ORIENTATION_FLIP_HORIZONTAL)!! + assertEquals(bmp.width, rotated.width) + assertEquals(bmp.height, rotated.height) + + assertEquals(Color.GREEN, rotated.getPixel(0, 0)) + assertEquals(Color.RED, rotated.getPixel(1, 0)) + assertEquals(Color.YELLOW, rotated.getPixel(0, 1)) + assertEquals(Color.BLUE, rotated.getPixel(1, 1)) + } + + @Test + fun testRotateBitmapViaExifWhenGivenFlipVerticalShouldReturnFlippedBitmap() { + val bmp = createTestBitmap() + val rotated = bmp.rotateBitmapViaExif(ExifInterface.ORIENTATION_FLIP_VERTICAL)!! + assertEquals(bmp.width, rotated.width) + assertEquals(bmp.height, rotated.height) + + assertEquals(Color.BLUE, rotated.getPixel(0, 0)) + assertEquals(Color.YELLOW, rotated.getPixel(1, 0)) + assertEquals(Color.RED, rotated.getPixel(0, 1)) + assertEquals(Color.GREEN, rotated.getPixel(1, 1)) + } +} diff --git a/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt index 535986c..853a0e4 100644 --- a/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt +++ b/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.extensions diff --git a/app/src/androidTest/java/com/nextcloud/extensions/GetExifOrientationTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/GetExifOrientationTests.kt new file mode 100644 index 0000000..3475c8e --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/extensions/GetExifOrientationTests.kt @@ -0,0 +1,78 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.extensions +import android.graphics.Bitmap +import android.graphics.Color +import androidx.exifinterface.media.ExifInterface +import com.nextcloud.utils.extensions.getExifOrientation +import junit.framework.TestCase.assertEquals +import org.junit.After +import org.junit.Test +import java.io.File + +class GetExifOrientationTests { + + private val tempFiles = mutableListOf() + + @Suppress("MagicNumber") + private fun createTempImageFile(): File { + val file = File.createTempFile("test_image", ".jpg") + tempFiles.add(file) + + val bmp = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888).apply { + setPixel(0, 0, Color.RED) + setPixel(1, 0, Color.GREEN) + setPixel(0, 1, Color.BLUE) + setPixel(1, 1, Color.YELLOW) + } + + file.outputStream().use { out -> + bmp.compress(Bitmap.CompressFormat.JPEG, 100, out) + } + + return file + } + + @After + fun cleanup() { + tempFiles.forEach { it.delete() } + } + + @Test + fun testGetExifOrientationWhenExifIsRotate90ShouldReturnRotate90() { + val file = createTempImageFile() + + val exif = ExifInterface(file.absolutePath) + exif.setAttribute(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90.toString()) + exif.saveAttributes() + + val orientation = getExifOrientation(file.absolutePath) + + assertEquals(ExifInterface.ORIENTATION_ROTATE_90, orientation) + } + + @Test + fun testGetExifOrientationWhenExifIsRotate180ShouldReturnRotate180() { + val file = createTempImageFile() + + val exif = ExifInterface(file.absolutePath) + exif.setAttribute(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_180.toString()) + exif.saveAttributes() + + val orientation = getExifOrientation(file.absolutePath) + assertEquals(ExifInterface.ORIENTATION_ROTATE_180, orientation) + } + + @Test + fun testGetExifOrientationWhenExifIsUndefinedShouldReturnUndefined() { + val file = createTempImageFile() + + val orientation = getExifOrientation(file.absolutePath) + assertEquals(ExifInterface.ORIENTATION_UNDEFINED, orientation) + } +} diff --git a/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt index 7a950a1..6fa385b 100644 --- a/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt +++ b/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.extensions diff --git a/app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt new file mode 100644 index 0000000..36600e6 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/extensions/StringExtensionTests.kt @@ -0,0 +1,176 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.extensions +import com.nextcloud.utils.extensions.isNotBlankAndEquals +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertTrue +import org.junit.Test + +@Suppress("TooManyFunctions") +class StringExtensionTests { + @Test + fun testIsNotBlankAndEqualsWhenGivenBothStringsAreNull() { + val str1: String? = null + val str2: String? = null + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenFirstStringIsNull() { + val str1: String? = null + val str2 = "hello" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenSecondStringIsNull() { + val str1 = "hello" + val str2: String? = null + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenBothStringsAreEmpty() { + val str1 = "" + val str2 = "" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenFirstStringIsEmpty() { + val str1 = "" + val str2 = "hello" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenSecondStringIsEmpty() { + val str1 = "hello" + val str2 = "" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenBothStringsAreWhitespaceOnly() { + val str1 = " " + val str2 = " \t " + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenFirstStringIsWhitespaceOnly() { + val str1 = " " + val str2 = "hello" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenSecondStringIsWhitespaceOnly() { + val str1 = "hello" + val str2 = " " + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenStringsAreDifferentButBothValid() { + val str1 = "hello" + val str2 = "world" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenStringsHaveDifferentCase() { + val str1 = "Hello" + val str2 = "hello" + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenMixedCaseStrings() { + val str1 = "HeLLo WoRLd" + val str2 = "hello world" + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenUppercaseStrings() { + val str1 = "HELLO" + val str2 = "hello" + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalAndValid() { + val str1 = "hello" + val str2 = "hello" + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalWithSpaces() { + val str1 = "hello world" + val str2 = "hello world" + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalSingleCharacter() { + val str1 = "a" + val str2 = "A" + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenBothStringsAreIdenticalWithSpecialCharacters() { + val str1 = "hello@world!123" + val str2 = "HELLO@WORLD!123" + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenOneHasLeadingWhitespaceAndOtherDoesNot() { + val str1 = " hello" + val str2 = "HELLO" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenOneHasTrailingWhitespaceAndOtherDoesNot() { + val str1 = "hello" + val str2 = "HELLO " + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenBothHaveIdenticalWhitespacePaddingDifferentCase() { + val str1 = " hello " + val str2 = " HELLO " + assertTrue(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenMixedWhitespaceCharacters() { + val str1 = "\t" + val str2 = "\n" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenOneIsNullAndOtherIsEmpty() { + val str1: String? = null + val str2 = "" + assertFalse(str1.isNotBlankAndEquals(str2)) + } + + @Test + fun testIsNotBlankAndEqualsWhenGivenOneIsNullAndOtherIsWhitespace() { + val str1: String? = null + val str2 = " " + assertFalse(str1.isNotBlankAndEquals(str2)) + } +} diff --git a/app/src/androidTest/java/com/nextcloud/sso/InputStreamBinderTest.kt b/app/src/androidTest/java/com/nextcloud/sso/InputStreamBinderTest.kt index 83898d5..a7ad3de 100644 --- a/app/src/androidTest/java/com/nextcloud/sso/InputStreamBinderTest.kt +++ b/app/src/androidTest/java/com/nextcloud/sso/InputStreamBinderTest.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.sso diff --git a/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt b/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt index 4876806..b310a89 100644 --- a/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt +++ b/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Álvaro Brey * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test diff --git a/app/src/androidTest/java/com/nextcloud/test/InjectionOverrideRule.kt b/app/src/androidTest/java/com/nextcloud/test/InjectionOverrideRule.kt index 3177c9f..0bb023f 100644 --- a/app/src/androidTest/java/com/nextcloud/test/InjectionOverrideRule.kt +++ b/app/src/androidTest/java/com/nextcloud/test/InjectionOverrideRule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test diff --git a/app/src/androidTest/java/com/nextcloud/test/InjectionTestActivityTest.kt b/app/src/androidTest/java/com/nextcloud/test/InjectionTestActivityTest.kt index d1294e2..76a3d19 100644 --- a/app/src/androidTest/java/com/nextcloud/test/InjectionTestActivityTest.kt +++ b/app/src/androidTest/java/com/nextcloud/test/InjectionTestActivityTest.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test diff --git a/app/src/androidTest/java/com/nextcloud/test/RandomStringGenerator.kt b/app/src/androidTest/java/com/nextcloud/test/RandomStringGenerator.kt index 6bba3d6..44a55e3 100644 --- a/app/src/androidTest/java/com/nextcloud/test/RandomStringGenerator.kt +++ b/app/src/androidTest/java/com/nextcloud/test/RandomStringGenerator.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test @@ -13,9 +13,7 @@ object RandomStringGenerator { @JvmOverloads @JvmStatic - fun make(length: Int = DEFAULT_LENGTH): String { - return (1..length) - .map { ALLOWED_CHARACTERS.random() } - .joinToString("") - } + fun make(length: Int = DEFAULT_LENGTH): String = (1..length) + .map { ALLOWED_CHARACTERS.random() } + .joinToString("") } diff --git a/app/src/androidTest/java/com/nextcloud/test/RetryTestRule.kt b/app/src/androidTest/java/com/nextcloud/test/RetryTestRule.kt index 720c514..45506db 100644 --- a/app/src/androidTest/java/com/nextcloud/test/RetryTestRule.kt +++ b/app/src/androidTest/java/com/nextcloud/test/RetryTestRule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test @@ -25,9 +25,7 @@ class RetryTestRule(val retryCount: Int = defaultRetryValue) : TestRule { private val defaultRetryValue: Int = if (BuildConfig.CI) 5 else 1 } - override fun apply(base: Statement, description: Description): Statement { - return statement(base, description) - } + override fun apply(base: Statement, description: Description): Statement = statement(base, description) @Suppress("TooGenericExceptionCaught") // and this exactly what we want here private fun statement(base: Statement, description: Description): Statement { diff --git a/app/src/androidTest/java/com/nextcloud/test/TestMainApp.kt b/app/src/androidTest/java/com/nextcloud/test/TestMainApp.kt index 398846a..912c7cd 100644 --- a/app/src/androidTest/java/com/nextcloud/test/TestMainApp.kt +++ b/app/src/androidTest/java/com/nextcloud/test/TestMainApp.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test diff --git a/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt b/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt index 509777f..37dcd1d 100644 --- a/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt +++ b/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test.model @@ -24,17 +24,11 @@ data class TestDataParcelable(val message: String) : Parcelable { parcel.writeString(message) } - override fun describeContents(): Int { - return 0 - } + override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): TestDataParcelable { - return TestDataParcelable(parcel) - } + override fun createFromParcel(parcel: Parcel): TestDataParcelable = TestDataParcelable(parcel) - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } + override fun newArray(size: Int): Array = arrayOfNulls(size) } } diff --git a/app/src/androidTest/java/com/nextcloud/ui/BitmapIT.kt b/app/src/androidTest/java/com/nextcloud/ui/BitmapIT.kt index 94f636d..5bf8a8f 100644 --- a/app/src/androidTest/java/com/nextcloud/ui/BitmapIT.kt +++ b/app/src/androidTest/java/com/nextcloud/ui/BitmapIT.kt @@ -1,124 +1,80 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui import android.graphics.BitmapFactory import android.widget.ImageView import android.widget.LinearLayout -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.R import com.owncloud.android.utils.BitmapUtils +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class BitmapIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + private val testClassName = "com.nextcloud.ui.BitmapIT" - @Test - @ScreenshotTest - fun roundBitmap() { - val file = getFile("christine.jpg") - val bitmap = BitmapFactory.decodeFile(file.absolutePath) - - val activity = testActivityRule.launchActivity(null) - val imageView = ImageView(activity).apply { - setImageBitmap(bitmap) - } - - val bitmap2 = BitmapFactory.decodeFile(file.absolutePath) - val imageView2 = ImageView(activity).apply { - setImageBitmap(BitmapUtils.roundBitmap(bitmap2)) - } - - val linearLayout = LinearLayout(activity).apply { - orientation = LinearLayout.VERTICAL - setBackgroundColor(context.getColor(R.color.grey_200)) - } - linearLayout.addView(imageView, 200, 200) - linearLayout.addView(imageView2, 200, 200) - activity.addView(linearLayout) - - screenshot(activity) + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) } - // @Test - // @ScreenshotTest - // fun glideSVG() { - // val activity = testActivityRule.launchActivity(null) - // val accountProvider = UserAccountManagerImpl.fromContext(activity) - // val clientFactory = ClientFactoryImpl(activity) - // - // val linearLayout = LinearLayout(activity).apply { - // orientation = LinearLayout.VERTICAL - // setBackgroundColor(context.getColor(R.color.grey_200)) - // } - // - // val file = getFile("christine.jpg") - // val bitmap = BitmapFactory.decodeFile(file.absolutePath) - // - // ImageView(activity).apply { - // setImageBitmap(bitmap) - // linearLayout.addView(this, 50, 50) - // } - // - // downloadIcon( - // client.baseUri.toString() + "/apps/files/img/app.svg", - // activity, - // linearLayout, - // accountProvider, - // clientFactory - // ) - // - // downloadIcon( - // client.baseUri.toString() + "/core/img/actions/group.svg", - // activity, - // linearLayout, - // accountProvider, - // clientFactory - // ) - // - // activity.addView(linearLayout) - // - // longSleep() - // - // screenshot(activity) - // } - // - // private fun downloadIcon( - // url: String, - // activity: TestActivity, - // linearLayout: LinearLayout, - // accountProvider: UserAccountManager, - // clientFactory: ClientFactory - // ) { - // val view = ImageView(activity).apply { - // linearLayout.addView(this, 50, 50) - // } - // val target = object : SimpleTarget() { - // override fun onResourceReady(resource: Drawable?, glideAnimation: GlideAnimation?) { - // view.setColorFilter(targetContext.getColor(R.color.dark), PorterDuff.Mode.SRC_ATOP) - // view.setImageDrawable(resource) - // } - // } - // - // testActivityRule.runOnUiThread { - // DisplayUtils.downloadIcon( - // accountProvider, - // clientFactory, - // activity, - // url, - // target, - // R.drawable.ic_user - // ) - // } - // } + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + @ScreenshotTest + fun roundBitmap() { + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val file = getFile("christine.jpg") + val bitmap = BitmapFactory.decodeFile(file.absolutePath) + + val imageView = ImageView(activity).apply { + setImageBitmap(bitmap) + } + + val bitmap2 = BitmapFactory.decodeFile(file.absolutePath) + val imageView2 = ImageView(activity).apply { + setImageBitmap(BitmapUtils.roundBitmap(bitmap2)) + } + + val linearLayout = LinearLayout(activity).apply { + orientation = LinearLayout.VERTICAL + setBackgroundColor(context.getColor(R.color.grey_200)) + } + linearLayout.addView(imageView, 200, 200) + linearLayout.addView(imageView2, 200, 200) + activity.addView(linearLayout) + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "roundBitmap", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(activity, screenShotName) + } + } + } + } } diff --git a/app/src/androidTest/java/com/nextcloud/ui/SetOnlineStatusBottomSheetIT.kt b/app/src/androidTest/java/com/nextcloud/ui/SetOnlineStatusBottomSheetIT.kt new file mode 100644 index 0000000..b799cab --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/ui/SetOnlineStatusBottomSheetIT.kt @@ -0,0 +1,56 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.ui + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.users.Status +import com.owncloud.android.lib.resources.users.StatusType +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.utils.EspressoIdlingResource +import org.junit.After +import org.junit.Before +import org.junit.Test + +class SetOnlineStatusBottomSheetIT : AbstractIT() { + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + fun open() { + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = SetOnlineStatusBottomSheet( + Status(StatusType.DND, "Working hard…", "🤖", -1) + ) + sut.show(activity.supportFragmentManager, "") + EspressoIdlingResource.decrement() + + onView(isRoot()).check(matches(isDisplayed())) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/nextcloud/ui/SetStatusDialogFragmentIT.kt b/app/src/androidTest/java/com/nextcloud/ui/SetStatusDialogFragmentIT.kt deleted file mode 100644 index 4a75751..0000000 --- a/app/src/androidTest/java/com/nextcloud/ui/SetStatusDialogFragmentIT.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.nextcloud.ui - -import androidx.test.espresso.intent.rule.IntentsTestRule -import com.owncloud.android.AbstractIT -import com.owncloud.android.lib.resources.users.ClearAt -import com.owncloud.android.lib.resources.users.PredefinedStatus -import com.owncloud.android.lib.resources.users.Status -import com.owncloud.android.lib.resources.users.StatusType -import com.owncloud.android.ui.activity.FileDisplayActivity -import org.junit.Rule -import org.junit.Test - -class SetStatusDialogFragmentIT : AbstractIT() { - @get:Rule - var activityRule = IntentsTestRule(FileDisplayActivity::class.java, true, false) - - @Test - fun open() { - val sut = SetStatusDialogFragment.newInstance(user, Status(StatusType.DND, "Working hard…", "🤖", -1)) - val activity = activityRule.launchActivity(null) - - sut.show(activity.supportFragmentManager, "") - - val predefinedStatus: ArrayList = arrayListOf( - PredefinedStatus("meeting", "📅", "In a meeting", ClearAt("period", "3600")), - PredefinedStatus("commuting", "🚌", "Commuting", ClearAt("period", "1800")), - PredefinedStatus("remote-work", "🏡", "Working remotely", ClearAt("end-of", "day")), - PredefinedStatus("sick-leave", "🤒", "Out sick", ClearAt("end-of", "day")), - PredefinedStatus("vacationing", "🌴", "Vacationing", null) - ) - - shortSleep() - - activity.runOnUiThread { sut.setPredefinedStatus(predefinedStatus) } - - longSleep() - } -} diff --git a/app/src/androidTest/java/com/nextcloud/ui/SetStatusMessageBottomSheetIT.kt b/app/src/androidTest/java/com/nextcloud/ui/SetStatusMessageBottomSheetIT.kt new file mode 100644 index 0000000..9e2fc9c --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/ui/SetStatusMessageBottomSheetIT.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.ui + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.users.ClearAt +import com.owncloud.android.lib.resources.users.PredefinedStatus +import com.owncloud.android.lib.resources.users.Status +import com.owncloud.android.lib.resources.users.StatusType +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.utils.EspressoIdlingResource +import org.junit.After +import org.junit.Before +import org.junit.Test + +class SetStatusMessageBottomSheetIT : AbstractIT() { + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + fun open() { + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = SetStatusMessageBottomSheet( + user, + Status(StatusType.DND, "Working hard…", "🤖", -1) + ) + sut.show(activity.supportFragmentManager, "") + val predefinedStatus: ArrayList = arrayListOf( + PredefinedStatus("meeting", "📅", "In a meeting", ClearAt("period", "3600")), + PredefinedStatus("commuting", "🚌", "Commuting", ClearAt("period", "1800")), + PredefinedStatus("be-right-back", "⏳", "Be right back", ClearAt("period", "900")), + PredefinedStatus("remote-work", "🏡", "Working remotely", ClearAt("end-of", "day")), + PredefinedStatus("sick-leave", "🤒", "Out sick", ClearAt("end-of", "day")), + PredefinedStatus("vacationing", "🌴", "Vacationing", null) + ) + sut.setPredefinedStatus(predefinedStatus) + EspressoIdlingResource.decrement() + + onView(isRoot()).check(matches(isDisplayed())) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt b/app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt new file mode 100644 index 0000000..68e99b9 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt @@ -0,0 +1,253 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import com.nextcloud.utils.autoRename.AutoRename +import com.owncloud.android.AbstractOnServerIT +import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile +import com.owncloud.android.lib.resources.status.CapabilityBooleanType +import com.owncloud.android.lib.resources.status.NextcloudVersion +import com.owncloud.android.lib.resources.status.OCCapability +import org.junit.Before +import org.junit.Test + +@Suppress("TooManyFunctions") +class AutoRenameTests : AbstractOnServerIT() { + + private var capability: OCCapability = fileDataStorageManager.getCapability(account.name) + private val forbiddenFilenameExtension = "." + private val forbiddenFilenameCharacter = ">" + + @Before + fun setup() { + testOnlyOnServer(NextcloudVersion.nextcloud_30) + + capability = capability.apply { + isWCFEnabled = CapabilityBooleanType.TRUE + forbiddenFilenameExtensionJson = listOf( + """[" ",".",".part",".part"]""", + """[".",".part",".part"," "]""", + """[".",".part"," ", ".part"]""", + """[".part"," ", ".part","."]""", + """[" ",".",".PART",".PART"]""", + """[".",".PART",".PART"," "]""", + """[".",".PART"," ", ".PART"]""", + """[".PART"," ", ".PART","."]""" + ).random() + forbiddenFilenameCharactersJson = """["<", ">", ":", "\\\\", "/", "|", "?", "*", "&"]""" + } + } + + @Test + fun testInvalidChar() { + val filename = "file${forbiddenFilenameCharacter}file.txt" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "file_file.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testInvalidExtension() { + val filename = "file$forbiddenFilenameExtension" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "file_" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testMultipleInvalidChars() { + val filename = "file|name?<>.txt" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "file_name___.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testStartEndInvalidExtensions() { + val filename = " .file.part " + val result = AutoRename.rename(filename, capability) + val expectedFilename = "_file_part" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testStartInvalidExtension() { + val filename = " .file.part" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "_file_part" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testEndInvalidExtension() { + val filename = ".file.part " + val result = AutoRename.rename(filename, capability) + val expectedFilename = "_file_part" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testMiddleNonPrintableChar() { + val filename = "file\u0001name.txt" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "filename.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testStartNonPrintableChar() { + val filename = "\u0001filename.txt" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "filename.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testEndNonPrintableChar() { + val filename = "filename.txt\u0001" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "filename.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testExtensionNonPrintableChar() { + val filename = "filename.t\u0001xt" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "filename.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testMiddleInvalidFolderChar() { + val folderPath = "abc/def/kg$forbiddenFilenameCharacter/lmo/pp/" + val result = AutoRename.rename(folderPath, capability) + val expectedFolderName = "abc/def/kg_/lmo/pp/" + assert(result == expectedFolderName) { "Expected $expectedFolderName but got $result" } + } + + @Test + fun testEndInvalidFolderChar() { + val folderPath = "abc/def/kg/lmo/pp$forbiddenFilenameCharacter/" + val result = AutoRename.rename(folderPath, capability) + val expectedFolderName = "abc/def/kg/lmo/pp_/" + assert(result == expectedFolderName) { "Expected $expectedFolderName but got $result" } + } + + @Test + fun testStartInvalidFolderChar() { + val folderPath = "${forbiddenFilenameCharacter}abc/def/kg/lmo/pp/" + val result = AutoRename.rename(folderPath, capability) + val expectedFolderName = "_abc/def/kg/lmo/pp/" + assert(result == expectedFolderName) { "Expected $expectedFolderName but got $result" } + } + + @Test + fun testMixedInvalidChar() { + val filename = " file\u0001na${forbiddenFilenameCharacter}me.txt " + val result = AutoRename.rename(filename, capability) + val expectedFilename = "filena_me.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testStartsWithPathSeparator() { + val folderPath = "/abc/def/kg/lmo/pp$forbiddenFilenameCharacter/file.txt/" + val result = AutoRename.rename(folderPath, capability) + val expectedFolderName = "/abc/def/kg/lmo/pp_/file.txt/" + assert(result == expectedFolderName) { "Expected $expectedFolderName but got $result" } + } + + @Test + fun testStartsWithPathSeparatorAndValidFilepath() { + val folderPath = "/COm02/2569.webp/" + val result = AutoRename.rename(folderPath, capability) + val expectedFolderName = "/COm02/2569.webp/" + assert(result == expectedFolderName) { "Expected $expectedFolderName but got $result" } + } + + @Test + fun testValidFilename() { + val filename = ".file.TXT" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "_file.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testRenameExtensionForFolder() { + val filename = "/Pictures/@User/SubDir/08.16.07 Ka Yel/" + val result = AutoRename.rename(filename, capability) + assert(result == filename) { "Expected $filename but got $result" } + } + + @Test + fun testRenameExtensionForFile() { + val filename = "/Pictures/@User/SubDir/08.16.07 Ka Yel.TXT" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "/Pictures/@User/SubDir/08.16.07 Ka Yel.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testE2EEFile() { + val decryptedFile = DecryptedFile( + authenticationTag = "HQlWBdm+gYC5kZwWnqXR1Q==", + filename = "a:a.jpg", + nonce = "sigyys8SfPZSScDJ860vYw==", + mimetype = "image/jpeg", + key = "sigyys8SfPZSScDJ860vYw==" + ) + + val result = AutoRename.rename(decryptedFile.filename, capability) + val expectedFilename = "a_a.jpg" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testRemovingLeadingWhitespace() { + val filename = " readme.txt" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "readme.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testRemovingTrailingWhitespace() { + val filename = "readme.txt " + val result = AutoRename.rename(filename, capability) + val expectedFilename = "readme.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testRemovingTrailingAndLeadingWhitespace() { + val filename = " readme.txt " + val result = AutoRename.rename(filename, capability) + val expectedFilename = "readme.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun testFolderNameLowercase() { + val filename = "Foo.Bar.Baz" + val result = AutoRename.rename(filename, capability, isFolderPath = true) + val expectedFilename = "Foo.Bar.Baz" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } + + @Test + fun skipAutoRenameWhenWCFDisabled() { + capability = capability.apply { + isWCFEnabled = CapabilityBooleanType.FALSE + } + val filename = " readme.txt " + val result = AutoRename.rename(filename, capability, isFolderPath = true) + assert(result == filename) { "Expected $filename but got $result" } + } +} diff --git a/app/src/androidTest/java/com/nextcloud/utils/CertificateValidatorTests.kt b/app/src/androidTest/java/com/nextcloud/utils/CertificateValidatorTests.kt new file mode 100644 index 0000000..1278585 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/utils/CertificateValidatorTests.kt @@ -0,0 +1,45 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import androidx.test.platform.app.InstrumentationRegistry +import com.google.gson.Gson +import com.owncloud.android.datamodel.Credentials +import com.owncloud.android.ui.dialog.setupEncryption.CertificateValidator +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.io.InputStreamReader + +class CertificateValidatorTests { + + private var sut: CertificateValidator? = null + + @Before + fun setup() { + sut = CertificateValidator() + } + + @After + fun destroy() { + sut = null + } + + @Test + fun testValidateWhenGivenValidServerKeyAndCertificateShouldReturnTrue() { + val inputStream = + InstrumentationRegistry.getInstrumentation().context.assets.open("credentials.json") + + val credentials = InputStreamReader(inputStream).use { reader -> + Gson().fromJson(reader, Credentials::class.java) + } + + val isCertificateValid = sut?.validate(credentials.publicKey, credentials.certificate) ?: false + assert(isCertificateValid) + } +} diff --git a/app/src/androidTest/java/com/nextcloud/utils/CheckWCFRestrictionsTests.kt b/app/src/androidTest/java/com/nextcloud/utils/CheckWCFRestrictionsTests.kt new file mode 100644 index 0000000..c16e705 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/utils/CheckWCFRestrictionsTests.kt @@ -0,0 +1,63 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.nextcloud.utils + +import com.nextcloud.utils.extensions.checkWCFRestrictions +import com.owncloud.android.lib.resources.status.CapabilityBooleanType +import com.owncloud.android.lib.resources.status.NextcloudVersion +import com.owncloud.android.lib.resources.status.OCCapability +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +@Suppress("MagicNumber") +class CheckWCFRestrictionsTests { + + private fun createCapability( + version: NextcloudVersion, + isWCFEnabled: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN + ): OCCapability = OCCapability().apply { + this.versionMayor = version.majorVersionNumber + this.isWCFEnabled = isWCFEnabled + } + + @Test + fun testReturnsFalseForVersionsOlderThan30() { + val capability = createCapability(NextcloudVersion.nextcloud_29) + assertFalse(capability.checkWCFRestrictions()) + } + + @Test + fun testReturnsTrueForVersion30WhenWCFAlwaysEnabled() { + val capability = createCapability(NextcloudVersion.nextcloud_30) + assertTrue(capability.checkWCFRestrictions()) + } + + @Test + fun testReturnsTrueForVersion31WhenWCFAlwaysEnabled() { + val capability = createCapability(NextcloudVersion.nextcloud_31) + assertTrue(capability.checkWCFRestrictions()) + } + + @Test + fun testReturnsTrueForVersion32WhenWCFEnabled() { + val capability = createCapability(NextcloudVersion.nextcloud_32, CapabilityBooleanType.TRUE) + assertTrue(capability.checkWCFRestrictions()) + } + + @Test + fun testReturnsFalseForVersion32WhenWCFDisabled() { + val capability = createCapability(NextcloudVersion.nextcloud_32, CapabilityBooleanType.FALSE) + assertFalse(capability.checkWCFRestrictions()) + } + + @Test + fun testReturnsFalseForVersion32WhenWCFIsUnknown() { + val capability = createCapability(NextcloudVersion.nextcloud_32) + assertFalse(capability.checkWCFRestrictions()) + } +} diff --git a/app/src/androidTest/java/com/nextcloud/utils/FileHelperTest.kt b/app/src/androidTest/java/com/nextcloud/utils/FileHelperTest.kt new file mode 100644 index 0000000..8f01315 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/utils/FileHelperTest.kt @@ -0,0 +1,204 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.nextcloud.utils + +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.io.File +import java.nio.file.Files + +@Suppress("TooManyFunctions") +class FileHelperTest { + + private lateinit var testDirectory: File + + @Before + fun setup() { + testDirectory = Files.createTempDirectory("test").toFile() + } + + @After + fun tearDown() { + testDirectory.deleteRecursively() + } + + @Test + fun testListDirectoryEntriesWhenGivenNullDirectoryShouldReturnEmptyList() { + val result = FileHelper.listDirectoryEntries(null, 0, 10, false) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenGivenNonExistentDirectoryShouldReturnEmptyList() { + val nonExistent = File(testDirectory, "does_not_exist") + val result = FileHelper.listDirectoryEntries(nonExistent, 0, 10, false) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenGivenFileInsteadOfDirectoryShouldReturnEmptyList() { + val file = File(testDirectory, "test.txt") + file.createNewFile() + val result = FileHelper.listDirectoryEntries(file, 0, 10, false) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenGivenEmptyDirectoryShouldReturnEmptyList() { + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 10, false) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenFetchingFoldersShouldReturnOnlyFolders() { + File(testDirectory, "folder1").mkdir() + File(testDirectory, "folder2").mkdir() + File(testDirectory, "file1.txt").createNewFile() + File(testDirectory, "file2.txt").createNewFile() + + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 10, true) + + assertEquals(2, result.size) + assertTrue(result.all { it.isDirectory }) + } + + @Test + fun testListDirectoryEntriesWhenFetchingFilesShouldReturnOnlyFiles() { + File(testDirectory, "folder1").mkdir() + File(testDirectory, "folder2").mkdir() + File(testDirectory, "file1.txt").createNewFile() + File(testDirectory, "file2.txt").createNewFile() + + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 10, false) + + assertEquals(2, result.size) + assertTrue(result.all { it.isFile }) + } + + @Test + fun testListDirectoryEntriesWhenStartIndexProvidedShouldSkipCorrectNumberOfItems() { + for (i in 1..5) File(testDirectory, "file$i.txt").createNewFile() + val result = FileHelper.listDirectoryEntries(testDirectory, 2, 10, false) + assertEquals(3, result.size) + } + + @Test + fun testListDirectoryEntriesWhenMaxItemsProvidedShouldLimitResults() { + for (i in 1..10) File(testDirectory, "file$i.txt").createNewFile() + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 5, false) + assertEquals(5, result.size) + } + + @Test + fun testListDirectoryEntriesWhenGivenStartIndexAndMaxItemsShouldReturnCorrectSubset() { + for (i in 1..10) File(testDirectory, "file$i.txt").createNewFile() + val result = FileHelper.listDirectoryEntries(testDirectory, 3, 4, false) + assertEquals(4, result.size) + } + + @Test + fun testListDirectoryEntriesWhenStartIndexBeyondAvailableShouldReturnEmptyList() { + for (i in 1..3) File(testDirectory, "file$i.txt").createNewFile() + val result = FileHelper.listDirectoryEntries(testDirectory, 10, 5, false) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenMaxItemsBeyondAvailableShouldReturnAllItems() { + for (i in 1..3) File(testDirectory, "file$i.txt").createNewFile() + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 100, false) + assertEquals(3, result.size) + } + + @Test + fun testListDirectoryEntriesWhenFetchingFoldersWithOffsetShouldSkipCorrectly() { + for (i in 1..5) File(testDirectory, "folder$i").mkdir() + for (i in 1..3) File(testDirectory, "file$i.txt").createNewFile() + + val result = FileHelper.listDirectoryEntries(testDirectory, 2, 10, true) + + assertEquals(3, result.size) + assertTrue(result.all { it.isDirectory }) + } + + @Test + fun testListDirectoryEntriesWhenFetchingFilesWithOffsetShouldSkipCorrectly() { + for (i in 1..3) File(testDirectory, "folder$i").mkdir() + for (i in 1..5) File(testDirectory, "file$i.txt").createNewFile() + + val result = FileHelper.listDirectoryEntries(testDirectory, 2, 10, false) + + assertEquals(3, result.size) + assertTrue(result.all { it.isFile }) + } + + @Test + fun testListDirectoryEntriesWhenGivenOnlyFoldersAndFetchingFilesShouldReturnEmptyList() { + for (i in 1..5) File(testDirectory, "folder$i").mkdir() + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 10, false) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenGivenOnlyFilesAndFetchingFoldersShouldReturnEmptyList() { + for (i in 1..5) File(testDirectory, "file$i.txt").createNewFile() + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 10, true) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenMaxItemsIsZeroShouldReturnEmptyList() { + for (i in 1..5) File(testDirectory, "file$i.txt").createNewFile() + val result = FileHelper.listDirectoryEntries(testDirectory, 0, 0, false) + assertTrue(result.isEmpty()) + } + + @Test + fun testListDirectoryEntriesWhenGivenMixedContentShouldFilterCorrectly() { + for (i in 1..3) File(testDirectory, "folder$i").mkdir() + for (i in 1..7) File(testDirectory, "file$i.txt").createNewFile() + + val folders = FileHelper.listDirectoryEntries(testDirectory, 0, 10, true) + val files = FileHelper.listDirectoryEntries(testDirectory, 0, 10, false) + + assertEquals(3, folders.size) + assertEquals(7, files.size) + assertTrue(folders.all { it.isDirectory }) + assertTrue(files.all { it.isFile }) + } + + @Test + fun testListDirectoryEntriesWhenPaginatingFoldersShouldWorkCorrectly() { + for (i in 1..10) File(testDirectory, "folder$i").mkdir() + + val page1 = FileHelper.listDirectoryEntries(testDirectory, 0, 3, true) + val page2 = FileHelper.listDirectoryEntries(testDirectory, 3, 3, true) + val page3 = FileHelper.listDirectoryEntries(testDirectory, 6, 3, true) + val page4 = FileHelper.listDirectoryEntries(testDirectory, 9, 3, true) + + assertEquals(3, page1.size) + assertEquals(3, page2.size) + assertEquals(3, page3.size) + assertEquals(1, page4.size) + } + + @Test + fun testListDirectoryEntriesWhenPaginatingFilesShouldWorkCorrectly() { + for (i in 1..10) File(testDirectory, "file$i.txt").createNewFile() + + val page1 = FileHelper.listDirectoryEntries(testDirectory, 0, 4, false) + val page2 = FileHelper.listDirectoryEntries(testDirectory, 4, 4, false) + val page3 = FileHelper.listDirectoryEntries(testDirectory, 8, 4, false) + + assertEquals(4, page1.size) + assertEquals(4, page2.size) + assertEquals(2, page3.size) + } +} diff --git a/app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt b/app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt new file mode 100644 index 0000000..89d4bb3 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt @@ -0,0 +1,243 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import com.nextcloud.utils.fileNameValidator.FileNameValidator +import com.owncloud.android.AbstractOnServerIT +import com.owncloud.android.R +import com.owncloud.android.lib.resources.status.CapabilityBooleanType +import com.owncloud.android.lib.resources.status.NextcloudVersion +import com.owncloud.android.lib.resources.status.OCCapability +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +@Suppress("TooManyFunctions") +class FileNameValidatorTests : AbstractOnServerIT() { + + private var capability: OCCapability = fileDataStorageManager.getCapability(account.name) + + @Before + fun setup() { + capability = capability.apply { + isWCFEnabled = CapabilityBooleanType.TRUE + forbiddenFilenamesJson = """[".htaccess",".htaccess"]""" + forbiddenFilenameBaseNamesJson = """ + ["con", "prn", "aux", "nul", "com0", "com1", "com2", "com3", "com4", + "com5", "com6", "com7", "com8", "com9", "com¹", "com²", "com³", + "lpt0", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", + "lpt8", "lpt9", "lpt¹", "lpt²", "lpt³"] + """ + forbiddenFilenameExtensionJson = """[" ",".",".part",".part"]""" + forbiddenFilenameCharactersJson = """["<", ">", ":", "\\\\", "/", "|", "?", "*", "&"]""" + } + } + + @Test + fun testInvalidCharacter() { + testOnlyOnServer(NextcloudVersion.nextcloud_30) + + val result = FileNameValidator.checkFileName("file + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import com.owncloud.android.datamodel.quickPermission.QuickPermissionType +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.lib.resources.shares.extensions.isAllowDownloadAndSyncEnabled +import com.owncloud.android.lib.resources.shares.extensions.toggleAllowDownloadAndSync +import com.owncloud.android.ui.fragment.util.SharePermissionManager +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertTrue +import org.junit.Test + +@Suppress("TooManyFunctions") +class SharePermissionManagerTest { + + private fun createShare(sharePermission: Int, isFolder: Boolean = false, attributesJson: String? = null): OCShare = + if (isFolder) { + OCShare("/test") + .apply { + permissions = sharePermission + attributes = attributesJson + shareType = ShareType.INTERNAL + sharedDate = 1188206955 + shareWith = "User 1" + sharedWithDisplayName = "User 1" + } + } else { + OCShare("/test.png") + .apply { + permissions = sharePermission + attributes = attributesJson + shareType = ShareType.INTERNAL + sharedDate = 1188206955 + shareWith = "User 1" + sharedWithDisplayName = "User 1" + } + }.apply { + this.isFolder = isFolder + } + + // region Permission change tests + @Test + fun testTogglePermissionShouldAddPermissionFlagWhenChecked() { + val initialPermission = OCShare.READ_PERMISSION_FLAG + val updatedPermission = + SharePermissionManager.togglePermission(true, initialPermission, OCShare.UPDATE_PERMISSION_FLAG) + val updatedShare = createShare(updatedPermission) + assertTrue(SharePermissionManager.isCustomPermission(updatedShare)) + } + + @Test + fun testTogglePermissionShouldRemovePermissionFlagWhenUnchecked() { + val initialPermission = OCShare.READ_PERMISSION_FLAG + OCShare.UPDATE_PERMISSION_FLAG + val updatedPermission = + SharePermissionManager.togglePermission(false, initialPermission, OCShare.UPDATE_PERMISSION_FLAG) + val updatedShare = createShare(updatedPermission) + assertTrue(SharePermissionManager.isViewOnly(updatedShare)) + } + // endregion + + // region HasPermissions tests + @Test + fun testHasPermissionShouldReturnTrueIfPermissionPresent() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.UPDATE_PERMISSION_FLAG + assertTrue(SharePermissionManager.hasPermission(permission, OCShare.UPDATE_PERMISSION_FLAG)) + } + + @Test + fun testHasPermissionShouldReturnFalseIfPermissionNotPresent() { + val permission = OCShare.READ_PERMISSION_FLAG + assertFalse(SharePermissionManager.hasPermission(permission, OCShare.UPDATE_PERMISSION_FLAG)) + } + // endregion + + // region Helper Method Tests + @Test + fun testCanEditShouldReturnTrueIfAllPermissionsPresent() { + val share = createShare(OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER, isFolder = true) + assertTrue(SharePermissionManager.canEdit(share)) + } + + @Test + fun testCanEditShouldReturnFalseIfPermissionsAreInsufficient() { + val share = createShare(OCShare.READ_PERMISSION_FLAG) + assertFalse(SharePermissionManager.canEdit(share)) + } + + @Test + fun testIsViewOnlyShouldReturnTrueIfOnlyReadPermissionSet() { + val share = createShare(OCShare.READ_PERMISSION_FLAG) + assertTrue(SharePermissionManager.isViewOnly(share)) + } + + @Test + fun testIsFileRequestShouldReturnTrueIfOnlyCreatePermissionSetOnFolder() { + val share = createShare(OCShare.CREATE_PERMISSION_FLAG, isFolder = true) + assertTrue(SharePermissionManager.isFileRequest(share)) + } + + @Test + fun testIsFileRequestShouldReturnFalseIfOnlyCreatePermissionSetOnFile() { + val share = createShare(OCShare.CREATE_PERMISSION_FLAG) + assertFalse(SharePermissionManager.isFileRequest(share)) + } + + @Test + fun testIsSecureFileDropShouldReturnTrueIfReadAndCreatePermissionsPresent() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.CREATE_PERMISSION_FLAG + val share = createShare(permission) + assertTrue(SharePermissionManager.isSecureFileDrop(share)) + } + + @Test + fun testCanReshareShouldReturnTrueIfSharePermissionIsPresent() { + val share = createShare(OCShare.SHARE_PERMISSION_FLAG) + assertTrue(SharePermissionManager.canReshare(share)) + } + + @Test + fun testGetMaximumPermissionForFolder() { + assertEquals( + OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER, + SharePermissionManager.getMaximumPermission(isFolder = true) + ) + } + + @Test + fun testGetMaximumPermissionForFile() { + assertEquals( + OCShare.MAXIMUM_PERMISSIONS_FOR_FILE, + SharePermissionManager.getMaximumPermission(isFolder = false) + ) + } + // endregion + + // region GetSelectedTypeTests + @Test + fun testGetSelectedTypeShouldReturnCanEditWhenFullPermissionsGiven() { + val share = createShare(OCShare.MAXIMUM_PERMISSIONS_FOR_FILE) + assertEquals(QuickPermissionType.CAN_EDIT, SharePermissionManager.getSelectedType(share, encrypted = false)) + } + + @Test + fun testGetSelectedTypeShouldReturnSecureFileDropWhenEncryptedAndReadCreateGiven() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.CREATE_PERMISSION_FLAG + val share = createShare(permission) + assertEquals( + QuickPermissionType.SECURE_FILE_DROP, + SharePermissionManager.getSelectedType(share, encrypted = true) + ) + } + + @Test + fun testGetSelectedTypeShouldReturnFileRequestWhenCreatePermissionGiven() { + val share = createShare(OCShare.CREATE_PERMISSION_FLAG, isFolder = true) + assertEquals(QuickPermissionType.FILE_REQUEST, SharePermissionManager.getSelectedType(share, encrypted = false)) + } + + @Test + fun testGetSelectedTypeShouldReturnViewOnlyWhenReadPermissionGiven() { + val share = createShare(OCShare.READ_PERMISSION_FLAG) + assertEquals(QuickPermissionType.VIEW_ONLY, SharePermissionManager.getSelectedType(share, encrypted = false)) + } + + @Test + fun testGetSelectedTypeShouldReturnCustomPermissionOnlyWhenCustomPermissionGiven() { + val share = createShare(OCShare.READ_PERMISSION_FLAG + OCShare.UPDATE_PERMISSION_FLAG) + assertEquals( + QuickPermissionType.CUSTOM_PERMISSIONS, + SharePermissionManager.getSelectedType(share, encrypted = false) + ) + } + + @Test + fun testGetSelectedTypeShouldReturnNoneOnlyWhenNoPermissionGiven() { + val share = createShare(OCShare.NO_PERMISSION) + assertEquals( + QuickPermissionType.NONE, + SharePermissionManager.getSelectedType(share, encrypted = false) + ) + } + // endregion + + // region CustomPermissions Tests + @Test + fun testIsCustomPermissionShouldReturnFalseWhenNoPermissionsGiven() { + val permission = OCShare.NO_PERMISSION + val share = createShare(permission, isFolder = false) + assertFalse(SharePermissionManager.isCustomPermission(share)) + } + + @Test + fun testIsCustomPermissionShouldReturnFalseWhenNoReadPermissionsGiven() { + val permission = OCShare.SHARE_PERMISSION_FLAG + OCShare.UPDATE_PERMISSION_FLAG + val share = createShare(permission, isFolder = false) + assertFalse(SharePermissionManager.isCustomPermission(share)) + } + + @Test + fun testIsCustomPermissionShouldReturnTrueWhenUpdatePermissionsGivenOnFile() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.UPDATE_PERMISSION_FLAG + val share = createShare(permission, isFolder = false) + assertTrue(SharePermissionManager.isCustomPermission(share)) + } + + @Test + fun testIsCustomPermissionShouldReturnTrueWhenUpdateAndSharePermissionsGivenOnFile() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.UPDATE_PERMISSION_FLAG + OCShare.SHARE_PERMISSION_FLAG + val share = createShare(permission, isFolder = false) + assertTrue(SharePermissionManager.isCustomPermission(share)) + } + + @Test + fun testIsCustomPermissionShouldReturnFalseWhenCreatePermissionsGivenOnFile() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.CREATE_PERMISSION_FLAG + val share = createShare(permission, isFolder = false) + assertFalse(SharePermissionManager.isCustomPermission(share)) + } + + @Test + fun testIsCustomPermissionShouldReturnFalseWhenDeletePermissionsGivenOnFile() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.DELETE_PERMISSION_FLAG + val share = createShare(permission, isFolder = false) + assertFalse(SharePermissionManager.isCustomPermission(share)) + } + + @Test + fun testIsCustomPermissionShouldReturnTrueWhenCreatePermissionsGivenOnFolder() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.CREATE_PERMISSION_FLAG + val share = createShare(permission, isFolder = true) + assertTrue(SharePermissionManager.isCustomPermission(share)) + } + + @Test + fun testIsCustomPermissionShouldReturnTrueWhenMixedPermissionsOnFile() { + val permission = OCShare.READ_PERMISSION_FLAG + OCShare.UPDATE_PERMISSION_FLAG + val share = createShare(permission, isFolder = false) + assertTrue(SharePermissionManager.isCustomPermission(share)) + } + // endregion + + // region Attributes Tests + @Test + fun testToggleAllowDownloadAndSyncShouldCreateAttributeJsonIfNoneExists() { + val ocShare = OCShare().apply { + isFolder = true + shareType = ShareType.USER + permissions = 17 + } + ocShare.attributes = toggleAllowDownloadAndSync( + ocShare.attributes, + isChecked = true, + useV2DownloadAttributes = false + ) + assertTrue(ocShare.isAllowDownloadAndSyncEnabled(false)) + } + + @Test + fun testIsAllowDownloadAndSyncEnabledShouldReturnFalseIfAttributeIsMissing() { + val share = createShare(OCShare.READ_PERMISSION_FLAG, attributesJson = null) + assertFalse(share.isAllowDownloadAndSyncEnabled(false)) + } + // endregion +} diff --git a/app/src/androidTest/java/com/nmc/android/ui/LauncherActivityIT.kt b/app/src/androidTest/java/com/nmc/android/ui/LauncherActivityIT.kt index 3523aec..921fd19 100644 --- a/app/src/androidTest/java/com/nmc/android/ui/LauncherActivityIT.kt +++ b/app/src/androidTest/java/com/nmc/android/ui/LauncherActivityIT.kt @@ -2,51 +2,76 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2023 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nmc.android.ui +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.owncloud.android.AbstractIT import com.owncloud.android.R -import org.junit.Rule +import com.owncloud.android.utils.EspressoIdlingResource +import org.junit.After +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LauncherActivityIT : AbstractIT() { - @get:Rule - val activityRule = ActivityScenarioRule(LauncherActivity::class.java) + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } - @Test - fun testSplashScreenWithEmptyTitlesShouldHideTitles() { - waitForIdleSync() - - onView(withId(R.id.ivSplash)).check(matches(isCompletelyDisplayed())) - - onView(withId(R.id.splashScreenBold)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))) - onView(withId(R.id.splashScreenNormal)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))) + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) } @Test - fun testSplashScreenWithTitlesShouldShowTitles() { - waitForIdleSync() - onView(withId(R.id.ivSplash)).check(matches(isCompletelyDisplayed())) - - activityRule.scenario.onActivity { - it.setSplashTitles("Example", "Cloud") + @UiThread + fun testSplashScreenWithEmptyTitlesShouldHideTitles() { + launchActivity().use { scenario -> + scenario.onActivity { _ -> + onIdleSync { + onView(withId(R.id.ivSplash)).check(matches(isCompletelyDisplayed())) + onView( + withId(R.id.splashScreenBold) + ).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))) + onView( + withId(R.id.splashScreenNormal) + ).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))) + } + } } + } - val onePercentArea = ViewMatchers.isDisplayingAtLeast(1) - onView(withId(R.id.splashScreenBold)).check(matches(onePercentArea)) - onView(withId(R.id.splashScreenNormal)).check(matches(onePercentArea)) + @Test + @UiThread + fun testSplashScreenWithTitlesShouldShowTitles() { + launchActivity().use { scenario -> + scenario.onActivity { + onIdleSync { + onView(withId(R.id.ivSplash)).check(matches(isCompletelyDisplayed())) + + EspressoIdlingResource.increment() + it.setSplashTitles("Example", "Cloud") + EspressoIdlingResource.decrement() + + val onePercentArea = ViewMatchers.isDisplayingAtLeast(1) + onView(withId(R.id.splashScreenBold)).check(matches(onePercentArea)) + onView(withId(R.id.splashScreenNormal)).check(matches(onePercentArea)) + } + } + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index 8360db1..f132bd0 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android; @@ -22,6 +22,8 @@ import android.view.View; import com.facebook.testing.screenshot.Screenshot; import com.facebook.testing.screenshot.internal.TestNameDetector; +import com.nextcloud.android.common.ui.theme.MaterialSchemes; +import com.nextcloud.android.common.ui.theme.MaterialSchemesImpl; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.account.UserAccountManagerImpl; @@ -38,7 +40,6 @@ import com.nextcloud.test.RandomStringGenerator; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.NameCollisionPolicy; @@ -54,6 +55,7 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.operations.CreateFolderOperation; import com.owncloud.android.operations.UploadFileOperation; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.theme.MaterialSchemesProvider; import org.apache.commons.io.FileUtils; import org.junit.After; @@ -182,7 +184,7 @@ public abstract class AbstractIT { String darkModeParameter = arguments.getString("DARKMODE"); if (darkModeParameter != null) { - if (darkModeParameter.equalsIgnoreCase("dark")) { + if ("dark".equalsIgnoreCase(darkModeParameter)) { DARK_MODE = "dark"; AppPreferencesImpl.fromContext(targetContext).setDarkThemeMode(DarkMode.DARK); MainApp.setAppTheme(DarkMode.DARK); @@ -191,7 +193,7 @@ public abstract class AbstractIT { } } - if (DARK_MODE.equalsIgnoreCase("light") && COLOR.equalsIgnoreCase("blue")) { + if ("light".equalsIgnoreCase(DARK_MODE) && "blue".equalsIgnoreCase(COLOR)) { // use already existing names DARK_MODE = ""; COLOR = ""; @@ -254,19 +256,12 @@ public abstract class AbstractIT { file.mkdirs(); return file; } else { - switch (name) { - case "empty.txt": - return createFile("empty.txt", 0); - - case "nonEmpty.txt": - return createFile("nonEmpty.txt", 100); - - case "chunkedFile.txt": - return createFile("chunkedFile.txt", 500000); - - default: - return createFile(name, 0); - } + return switch (name) { + case "empty.txt" -> createFile("empty.txt", 0); + case "nonEmpty.txt" -> createFile("nonEmpty.txt", 100); + case "chunkedFile.txt" -> createFile("chunkedFile.txt", 500000); + default -> createFile(name, 0); + }; } } @@ -301,7 +296,7 @@ public abstract class AbstractIT { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } - protected void onIdleSync(Runnable recipient) { + public void onIdleSync(Runnable recipient) { InstrumentationRegistry.getInstrumentation().waitForIdle(recipient); } @@ -355,7 +350,7 @@ public abstract class AbstractIT { } } - public OCFile createFolder(String remotePath) { + public void createFolder(String remotePath) { RemoteOperationResult check = new ExistenceCheckRemoteOperation(remotePath, false).execute(client); if (!check.isSuccess()) { @@ -363,8 +358,6 @@ public abstract class AbstractIT { .execute(client) .isSuccess()); } - - return getStorageManager().getFileByDecryptedRemotePath(remotePath.endsWith("/") ? remotePath : remotePath + "/"); } public void uploadFile(File file, String remotePath) { @@ -375,6 +368,11 @@ public abstract class AbstractIT { public void uploadOCUpload(OCUpload ocUpload) { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public void isNetworkAndServerAvailable(@NonNull GenericCallback callback) { + + } + @Override public boolean isConnected() { return false; @@ -457,12 +455,18 @@ public abstract class AbstractIT { screenshot(view, ""); } - protected void screenshotViaName(Activity activity, String name) { + public void screenshotViaName(Activity activity, String name) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { Screenshot.snapActivity(activity).setName(name).record(); } } + protected void screenshotViaName(View view, String name) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + Screenshot.snap(view).setName(name).record(); + } + } + protected void screenshot(View view, String prefix) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { Screenshot.snap(view).setName(createName(prefix)).record(); @@ -541,4 +545,38 @@ public abstract class AbstractIT { protected static boolean removeAccount(Account account) { return AccountManager.get(targetContext).removeAccountExplicitly(account); } + + protected MaterialSchemesProvider getMaterialSchemesProvider() { + return new MaterialSchemesProvider() { + @NonNull + @Override + public MaterialSchemes getMaterialSchemesForUser(@NonNull User user) { + return null; + } + + @NonNull + @Override + public MaterialSchemes getMaterialSchemesForCapability(@NonNull OCCapability capability) { + return null; + } + + @NonNull + @Override + public MaterialSchemes getMaterialSchemesForCurrentUser() { + return new MaterialSchemesImpl(R.color.primary, false); + } + + @NonNull + @Override + public MaterialSchemes getDefaultMaterialSchemes() { + return null; + } + + @NonNull + @Override + public MaterialSchemes getMaterialSchemesForPrimaryBackground() { + return null; + } + }; + } } diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index 78d5acd..3a25e46 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android; @@ -13,7 +13,6 @@ import android.accounts.OperationCanceledException; import android.content.ActivityNotFoundException; import android.net.Uri; import android.os.Bundle; - import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.account.UserAccountManagerImpl; @@ -174,9 +173,7 @@ public abstract class AbstractOnServerIT extends AbstractIT { Assert.fail("Server not ready!"); } - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { + } catch (IOException | InterruptedException e) { e.printStackTrace(); } } @@ -187,6 +184,11 @@ public abstract class AbstractOnServerIT extends AbstractIT { public void uploadOCUpload(OCUpload ocUpload, int localBehaviour) { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public void isNetworkAndServerAvailable(@NonNull GenericCallback callback) { + + } + @Override public boolean isConnected() { return false; diff --git a/app/src/androidTest/java/com/owncloud/android/DownloadIT.java b/app/src/androidTest/java/com/owncloud/android/DownloadIT.java index 7ecd8da..610469f 100644 --- a/app/src/androidTest/java/com/owncloud/android/DownloadIT.java +++ b/app/src/androidTest/java/com/owncloud/android/DownloadIT.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android; diff --git a/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt index ed119a2..ec53f46 100644 --- a/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt @@ -1,8 +1,8 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android diff --git a/app/src/androidTest/java/com/owncloud/android/FileIT.java b/app/src/androidTest/java/com/owncloud/android/FileIT.java index ff7ef82..2ba3622 100644 --- a/app/src/androidTest/java/com/owncloud/android/FileIT.java +++ b/app/src/androidTest/java/com/owncloud/android/FileIT.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android; @@ -106,8 +106,8 @@ public class FileIT extends AbstractOnServerIT { assertTrue(new SynchronizeFolderOperation(targetContext, folderPath, user, - System.currentTimeMillis(), - fileDataStorageManager) + fileDataStorageManager, + false) .execute(targetContext) .isSuccess()); diff --git a/app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java b/app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java deleted file mode 100644 index f7b95f9..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2018 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android; - -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.operations.CreateFolderOperation; -import com.owncloud.android.operations.common.SyncOperation; -import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.ui.activity.SettingsActivity; -import com.owncloud.android.ui.activity.SyncedFoldersActivity; - -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.espresso.action.ViewActions; -import androidx.test.espresso.contrib.DrawerActions; -import androidx.test.espresso.contrib.RecyclerViewActions; -import androidx.test.espresso.matcher.PreferenceMatchers; -import androidx.test.filters.LargeTest; -import tools.fastlane.screengrab.Screengrab; -import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy; -import tools.fastlane.screengrab.locale.LocaleTestRule; - -import static androidx.test.espresso.Espresso.onData; -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.Espresso.pressBack; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.core.AnyOf.anyOf; -import static org.junit.Assert.assertTrue; - -@LargeTest -@RunWith(JUnit4.class) -public class ScreenshotsIT extends AbstractOnServerIT { - @ClassRule - public static final LocaleTestRule localeTestRule = new LocaleTestRule(); - - @BeforeClass - public static void beforeScreenshot() { - Screengrab.setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy()); - } - - @Test - public void gridViewScreenshot() { - ActivityScenario.launch(FileDisplayActivity.class); - - onView(anyOf(withText(R.string.action_switch_grid_view), withId(R.id.switch_grid_view_button))).perform(click()); - - shortSleep(); - - Screengrab.screenshot("01_gridView"); - - onView(anyOf(withText(R.string.action_switch_list_view), withId(R.id.switch_grid_view_button))).perform(click()); - - Assert.assertTrue(true); // if we reach this, everything is ok - } - - @Test - public void listViewScreenshot() { - String path = "/Camera/"; - - // folder does not exist yet - if (getStorageManager().getFileByEncryptedRemotePath(path) == null) { - SyncOperation syncOp = new CreateFolderOperation(path, user, targetContext, getStorageManager()); - RemoteOperationResult result = syncOp.execute(client); - - assertTrue(result.isSuccess()); - } - - ActivityScenario.launch(FileDisplayActivity.class); - - // go into work folder - onView(withId(R.id.list_root)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click())); - - Screengrab.screenshot("02_listView"); - - Assert.assertTrue(true); // if we reach this, everything is ok - } - - @Test - public void drawerScreenshot() { - ActivityScenario.launch(FileDisplayActivity.class); - - onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()); - - Screengrab.screenshot("03_drawer"); - - onView(withId(R.id.drawer_layout)).perform(DrawerActions.close()); - - Assert.assertTrue(true); // if we reach this, everything is ok - } - - @Test - public void multipleAccountsScreenshot() { - ActivityScenario.launch(FileDisplayActivity.class); - - onView(withId(R.id.switch_account_button)).perform(click()); - - Screengrab.screenshot("04_accounts"); - - pressBack(); - - Assert.assertTrue(true); // if we reach this, everything is ok - } - - @Test - public void autoUploadScreenshot() { - ActivityScenario.launch(SyncedFoldersActivity.class); - - Screengrab.screenshot("05_autoUpload"); - - Assert.assertTrue(true); // if we reach this, everything is ok - } - - @Test - public void davdroidScreenshot() { - ActivityScenario.launch(SettingsActivity.class); - - onData(PreferenceMatchers.withTitle(R.string.prefs_category_more)).perform(ViewActions.scrollTo()); - - shortSleep(); - - Screengrab.screenshot("06_davdroid"); - - Assert.assertTrue(true); // if we reach this, everything is ok - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.kt b/app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.kt new file mode 100644 index 0000000..3edb4f5 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.kt @@ -0,0 +1,183 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.DrawerActions +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.activity.SettingsActivity +import com.owncloud.android.ui.activity.SyncedFoldersActivity +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Test +import tools.fastlane.screengrab.Screengrab +import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy +import tools.fastlane.screengrab.locale.LocaleTestRule + +class ScreenshotsIT : AbstractIT() { + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + @ScreenshotTest + fun gridViewScreenshot() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + onView(withId(R.id.switch_grid_view_button)).perform(click()) + EspressoIdlingResource.decrement() + + onView(isRoot()).check(matches(isDisplayed())) + Screengrab.screenshot("01_gridView") + + // Switch back + onView(withId(R.id.switch_grid_view_button)).perform(click()) + + assertTrue(true) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun listViewScreenshot() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val path = "/Camera/" + OCFile(path).apply { + storageManager.saveFile(this) + } + onView(withId(R.id.list_root)).perform(click()) + EspressoIdlingResource.decrement() + + onView(isRoot()).check(matches(isDisplayed())) + Screengrab.screenshot("02_listView") + assertTrue(true) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun drawerScreenshot() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) + EspressoIdlingResource.decrement() + + onView(isRoot()).check(matches(isDisplayed())) + Screengrab.screenshot("03_drawer") + + onView(withId(R.id.drawer_layout)).perform(DrawerActions.close()) + assertTrue(true) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun multipleAccountsScreenshot() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + onView(withId(R.id.switch_account_button)).perform(click()) + EspressoIdlingResource.decrement() + + onView(isRoot()).check(matches(isDisplayed())) + Screengrab.screenshot("04_accounts") + + Espresso.pressBack() + assertTrue(true) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun autoUploadScreenshot() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + onView(isRoot()).check(matches(isDisplayed())) + Screengrab.screenshot("05_autoUpload") + assertTrue(true) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun davdroidScreenshot() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + onView(withText(R.string.prefs_category_more)).perform(scrollTo()) + EspressoIdlingResource.decrement() + + onView(isRoot()).check(matches(isDisplayed())) + Screengrab.screenshot("06_davdroid") + assertTrue(true) + } + } + } + } + + companion object { + @ClassRule + @JvmField + val localeTestRule: LocaleTestRule = LocaleTestRule() + + @BeforeClass + @JvmStatic + fun beforeScreenshot() { + Screengrab.setDefaultScreenshotStrategy(UiAutomatorScreenshotStrategy()) + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/UploadIT.java b/app/src/androidTest/java/com/owncloud/android/UploadIT.java index cd5d003..cd3980b 100644 --- a/app/src/androidTest/java/com/owncloud/android/UploadIT.java +++ b/app/src/androidTest/java/com/owncloud/android/UploadIT.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android; @@ -56,6 +56,11 @@ public class UploadIT extends AbstractOnServerIT { targetContext.getContentResolver()); private ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public void isNetworkAndServerAvailable(@NonNull GenericCallback callback) { + + } + @Override public boolean isConnected() { return false; @@ -274,6 +279,11 @@ public class UploadIT extends AbstractOnServerIT { @Test public void testUploadOnWifiOnlyButNoWifi() { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public void isNetworkAndServerAvailable(@NonNull GenericCallback callback) { + + } + @Override public boolean isConnected() { return false; @@ -358,6 +368,11 @@ public class UploadIT extends AbstractOnServerIT { @Test public void testUploadOnWifiOnlyButMeteredWifi() { ConnectivityService connectivityServiceMock = new ConnectivityService() { + @Override + public void isNetworkAndServerAvailable(@NonNull GenericCallback callback) { + + } + @Override public boolean isConnected() { return false; @@ -452,7 +467,7 @@ public class UploadIT extends AbstractOnServerIT { assertEquals(remotePath, ocFile.getRemotePath()); assertEquals(creationTimestamp, ocFile.getCreationTimestamp()); - assertTrue(uploadTimestamp - 10 < ocFile.getUploadTimestamp() || + assertTrue(uploadTimestamp - 10 < ocFile.getUploadTimestamp() && uploadTimestamp + 10 > ocFile.getUploadTimestamp()); } @@ -498,7 +513,7 @@ public class UploadIT extends AbstractOnServerIT { OCFile ocFile = null; for (OCFile f : files) { - if (f.getFileName().equals("metadata.jpg")) { + if ("metadata.jpg".equals(f.getFileName())) { ocFile = f; break; } diff --git a/app/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt index 996ea51..0ed8b7f 100644 --- a/app/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/authentication/AuthenticatorActivityIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.authentication diff --git a/app/src/androidTest/java/com/owncloud/android/authentication/PassCodeManagerIT.kt b/app/src/androidTest/java/com/owncloud/android/authentication/PassCodeManagerIT.kt index d8ab5e2..7d616c4 100644 --- a/app/src/androidTest/java/com/owncloud/android/authentication/PassCodeManagerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/authentication/PassCodeManagerIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.authentication diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/ArbitraryDataProviderIT.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/ArbitraryDataProviderIT.kt index 2de4267..6188d5e 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/ArbitraryDataProviderIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/ArbitraryDataProviderIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/ContentResolverHelperIT.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/ContentResolverHelperIT.kt index d2c9e0b..1a5624f 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/ContentResolverHelperIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/ContentResolverHelperIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Álvaro Brey * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel @@ -43,7 +43,6 @@ class ContentResolverHelperIT { } @Test - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O) fun contentResolver_onAndroid26_usesNewAPI() { ContentResolverHelper .queryResolver(resolver, URI, PROJECTION, SELECTION, null, SORT_COLUMN, SORT_DIRECTION, LIMIT) diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/Credentials.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/Credentials.kt new file mode 100644 index 0000000..5bcaf7c --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/Credentials.kt @@ -0,0 +1,10 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.datamodel + +data class Credentials(val publicKey: String, val certificate: String) diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentProviderClientIT.java b/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentProviderClientIT.java index 3e435d3..76c576c 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentProviderClientIT.java +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentProviderClientIT.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentResolverIT.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentResolverIT.kt index 3bb41ef..6c800da 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentResolverIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerContentResolverIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerIT.java b/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerIT.java index f3bae17..34f8061 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/FileDataStorageManagerIT.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt index baf2c61..121d630 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt index 0c130f9..59167c6 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java b/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java index 1c73a50..54efa0c 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java @@ -36,6 +36,7 @@ public class OCFileUnitTest { private static final String STORAGE_PATH = "/mnt/sd/localpath/to/a/file.txt"; private static final String MIME_TYPE = "text/plain"; private static final long FILE_LENGTH = 9876543210L; + private static final long UPLOADED_TIMESTAMP = 8765431109L; private static final long CREATION_TIMESTAMP = 8765432109L; private static final long MODIFICATION_TIMESTAMP = 7654321098L; private static final long MODIFICATION_TIMESTAMP_AT_LAST_SYNC_FOR_DATA = 6543210987L; @@ -63,6 +64,7 @@ public class OCFileUnitTest { mFile.setStoragePath(STORAGE_PATH); mFile.setMimeType(MIME_TYPE); mFile.setFileLength(FILE_LENGTH); + mFile.setUploadTimestamp(UPLOADED_TIMESTAMP); mFile.setCreationTimestamp(CREATION_TIMESTAMP); mFile.setModificationTimestamp(MODIFICATION_TIMESTAMP); mFile.setModificationTimestampAtLastSyncForData(MODIFICATION_TIMESTAMP_AT_LAST_SYNC_FOR_DATA); @@ -93,6 +95,7 @@ public class OCFileUnitTest { assertThat(fileReadFromParcel.getStoragePath(), is(STORAGE_PATH)); assertThat(fileReadFromParcel.getMimeType(), is(MIME_TYPE)); assertThat(fileReadFromParcel.getFileLength(), is(FILE_LENGTH)); + assertThat(fileReadFromParcel.getUploadTimestamp(), is(UPLOADED_TIMESTAMP)); assertThat(fileReadFromParcel.getCreationTimestamp(), is(CREATION_TIMESTAMP)); assertThat(fileReadFromParcel.getModificationTimestamp(), is(MODIFICATION_TIMESTAMP)); assertThat( diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java b/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java index 2bf5702..3d7d52b 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2017 JARP * SPDX-FileCopyrightText: 2021 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.datamodel; diff --git a/app/src/androidTest/java/com/owncloud/android/extensions/AbstractITExtensions.kt b/app/src/androidTest/java/com/owncloud/android/extensions/AbstractITExtensions.kt new file mode 100644 index 0000000..f6b6623 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/extensions/AbstractITExtensions.kt @@ -0,0 +1,39 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.extensions + +import android.app.Activity +import android.content.Intent +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed + +inline fun AbstractIT.launchAndCapture( + testClassName: String, + actionName: String, + intent: Intent? = null, + crossinline before: (T) -> Unit +) { + launchActivity(intent).use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + before(activity) + EspressoIdlingResource.decrement() + + val screenshotName = createName(testClassName + "_" + actionName, "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(activity, screenshotName) + } + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt b/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt index 7a7ea10..5de86fc 100644 --- a/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas + * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.owncloud.android.files @@ -331,10 +331,7 @@ class FileMenuFilterIT : AbstractIT() { } } - private data class ExpectedLockVisibilities( - val lockFile: Boolean, - val unlockFile: Boolean - ) + private data class ExpectedLockVisibilities(val lockFile: Boolean, val unlockFile: Boolean) private fun configureCapability(capability: OCCapability) { every { mockStorageManager.getCapability(any()) } returns capability diff --git a/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt b/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt index 13b04c0..3f8f73e 100644 --- a/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.files.services @@ -34,9 +34,9 @@ abstract class FileUploaderIT : AbstractOnServerIT() { private var uploadsStorageManager: UploadsStorageManager? = null private val connectivityServiceMock: ConnectivityService = object : ConnectivityService { - override fun isConnected(): Boolean { - return false - } + override fun isNetworkAndServerAvailable(callback: ConnectivityService.GenericCallback) = Unit + + override fun isConnected(): Boolean = false override fun isInternetWalled(): Boolean = false override fun getConnectivity(): Connectivity = Connectivity.CONNECTED_WIFI diff --git a/app/src/androidTest/java/com/owncloud/android/files/services/LegacyFileUploaderIT.kt b/app/src/androidTest/java/com/owncloud/android/files/services/LegacyFileUploaderIT.kt deleted file mode 100644 index e8cc664..0000000 --- a/app/src/androidTest/java/com/owncloud/android/files/services/LegacyFileUploaderIT.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2022 Tobias Kaminsky - * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.files.services - -class LegacyFileUploaderIT : FileUploaderIT() diff --git a/app/src/androidTest/java/com/owncloud/android/operations/GetSharesForFileOperationIT.kt b/app/src/androidTest/java/com/owncloud/android/operations/GetSharesForFileOperationIT.kt index 5685531..1d268ce 100644 --- a/app/src/androidTest/java/com/owncloud/android/operations/GetSharesForFileOperationIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/operations/GetSharesForFileOperationIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations diff --git a/app/src/androidTest/java/com/owncloud/android/operations/RemoveFileOperationIT.java b/app/src/androidTest/java/com/owncloud/android/operations/RemoveFileOperationIT.java index c2adc36..33f0910 100644 --- a/app/src/androidTest/java/com/owncloud/android/operations/RemoveFileOperationIT.java +++ b/app/src/androidTest/java/com/owncloud/android/operations/RemoveFileOperationIT.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.operations; diff --git a/app/src/androidTest/java/com/owncloud/android/providers/DocumentsProviderUtils.kt b/app/src/androidTest/java/com/owncloud/android/providers/DocumentsProviderUtils.kt index 0a130da..cd5f695 100644 --- a/app/src/androidTest/java/com/owncloud/android/providers/DocumentsProviderUtils.kt +++ b/app/src/androidTest/java/com/owncloud/android/providers/DocumentsProviderUtils.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Torsten Grote - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.providers @@ -177,31 +177,30 @@ object DocumentsProviderUtils { */ @Suppress("EXPERIMENTAL_API_USAGE") @VisibleForTesting - internal suspend fun getLoadedCursor(timeout: Long = 15_000, query: () -> Cursor?) = - withTimeout(timeout) { - suspendCancellableCoroutine { cont -> - val cursor = query() ?: throw IOException("Initial query returned no results") - cont.invokeOnCancellation { cursor.close() } - val loading = cursor.extras.getBoolean(EXTRA_LOADING, false) - if (loading) { - Log_OC.e("TEST", "Cursor was loading, wait for update...") - cursor.registerContentObserver( - object : ContentObserver(null) { - override fun onChange(selfChange: Boolean, uri: Uri?) { - cursor.close() - val newCursor = query() - if (newCursor == null) { - cont.cancel(IOException("Re-query returned no results")) - } else { - cont.resume(newCursor) - } + internal suspend fun getLoadedCursor(timeout: Long = 15_000, query: () -> Cursor?) = withTimeout(timeout) { + suspendCancellableCoroutine { cont -> + val cursor = query() ?: throw IOException("Initial query returned no results") + cont.invokeOnCancellation { cursor.close() } + val loading = cursor.extras?.getBoolean(EXTRA_LOADING, false) ?: false + if (loading) { + Log_OC.e("TEST", "Cursor was loading, wait for update...") + cursor.registerContentObserver( + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean, uri: Uri?) { + cursor.close() + val newCursor = query() + if (newCursor == null) { + cont.cancel(IOException("Re-query returned no results")) + } else { + cont.resume(newCursor) } } - ) - } else { - // not loading, return cursor right away - cont.resume(cursor) - } + } + ) + } else { + // not loading, return cursor right away + cont.resume(cursor) } } + } } diff --git a/app/src/androidTest/java/com/owncloud/android/providers/DocumentsStorageProviderIT.kt b/app/src/androidTest/java/com/owncloud/android/providers/DocumentsStorageProviderIT.kt index 7510497..3bdd0e0 100644 --- a/app/src/androidTest/java/com/owncloud/android/providers/DocumentsStorageProviderIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/providers/DocumentsStorageProviderIT.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Torsten Grote - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.providers @@ -53,6 +53,14 @@ class DocumentsStorageProviderIT : AbstractOnServerIT() { // DocumentsProvider#onCreate() is called when the application is started // which is *after* AbstractOnServerIT adds the accounts (when the app is freshly installed). // So we need to query our roots here to ensure that the internal storage map is initialized. + storageManager.run { + val updatedRootPath = getFileByEncryptedRemotePath(ROOT_PATH).apply { + permissions = "RSMCKGWDNV" + } + + saveFile(updatedRootPath) + } + contentResolver.query(DocumentsContract.buildRootsUri(authority), null, null, null) assertTrue("Storage root does not exist", rootDir.exists()) assertTrue(rootDir.isDirectory) diff --git a/app/src/androidTest/java/com/owncloud/android/providers/FileContentProviderVerificationIT.kt b/app/src/androidTest/java/com/owncloud/android/providers/FileContentProviderVerificationIT.kt index 3dbf7fa..573f320 100644 --- a/app/src/androidTest/java/com/owncloud/android/providers/FileContentProviderVerificationIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/providers/FileContentProviderVerificationIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.providers diff --git a/app/src/androidTest/java/com/owncloud/android/providers/UsersAndGroupsSearchProviderIT.kt b/app/src/androidTest/java/com/owncloud/android/providers/UsersAndGroupsSearchProviderIT.kt index 1be0e70..0e660d2 100644 --- a/app/src/androidTest/java/com/owncloud/android/providers/UsersAndGroupsSearchProviderIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/providers/UsersAndGroupsSearchProviderIT.kt @@ -1,32 +1,32 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.providers -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractOnServerIT -import org.junit.Rule import org.junit.Test class UsersAndGroupsSearchProviderIT : AbstractOnServerIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) - @Test + @UiThread fun searchUser() { - val activity = testActivityRule.launchActivity(null) - - shortSleep() - - activity.runOnUiThread { - // fragment.search("Admin") + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + onView(isRoot()).check(matches(isDisplayed())) + } + } } - - longSleep() } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt index 42924cd..9601da9 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui @@ -45,14 +45,13 @@ class LoginIT : AbstractIT() { ActivityScenario.launch(AuthenticatorActivity::class.java) } - @Test - @Throws(InterruptedException::class) - @Suppress("MagicNumber", "SwallowedException") - /** * The CI/CD pipeline is encountering issues related to the Android version for this functionality. * Therefore the test will only be executed on Android versions 10 and above. */ + @Test + @Throws(InterruptedException::class) + @Suppress("MagicNumber", "SwallowedException") @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) fun login() { val arguments = InstrumentationRegistry.getArguments() @@ -71,7 +70,7 @@ class LoginIT : AbstractIT() { Web.onWebView() .withElement(DriverAtoms.findElement(Locator.XPATH, "//form[@id='login-form']/input[@type='submit']")) .perform(DriverAtoms.webClick()) - } catch (e: RuntimeException) { + } catch (_: RuntimeException) { // NC < 25 Web.onWebView() .withElement(DriverAtoms.findElement(Locator.XPATH, "//p[@id='redirect-link']/a")) @@ -94,7 +93,7 @@ class LoginIT : AbstractIT() { Web.onWebView() .withElement(DriverAtoms.findElement(Locator.XPATH, "//button[@type='submit']")) .perform(DriverAtoms.webClick()) - } catch (e: RuntimeException) { + } catch (_: RuntimeException) { // NC < 25 Web.onWebView() .withElement(DriverAtoms.findElement(Locator.XPATH, "//input[@type='submit']")) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java b/app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java deleted file mode 100644 index 4ac55e6..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.activity; - -import android.content.Intent; - -import com.nextcloud.client.account.UserAccountManagerImpl; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.db.OCUpload; -import com.owncloud.android.ui.dialog.ConflictsResolveDialog; -import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; - -import java.util.Objects; - -import androidx.fragment.app.DialogFragment; -import androidx.test.espresso.intent.rule.IntentsTestRule; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertEquals; - -public class ConflictsResolveActivityIT extends AbstractIT { - @Rule public IntentsTestRule activityRule = - new IntentsTestRule<>(ConflictsResolveActivity.class, true, false); - private boolean returnCode; - - @Test - @ScreenshotTest - public void screenshotTextFiles() { - OCFile newFile = new OCFile("/newFile.txt"); - newFile.setFileLength(56000); - newFile.setModificationTimestamp(1522019340); - newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt"); - - OCFile existingFile = new OCFile("/newFile.txt"); - existingFile.setFileLength(1024000); - existingFile.setModificationTimestamp(1582019340); - - FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver()); - storageManager.saveNewFile(existingFile); - - Intent intent = new Intent(targetContext, ConflictsResolveActivity.class); - intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile); - - ConflictsResolveActivity sut = activityRule.launchActivity(intent); - - ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile, - newFile, - UserAccountManagerImpl - .fromContext(targetContext) - .getUser() - ); - dialog.showDialog(sut); - - getInstrumentation().waitForIdleSync(); - - shortSleep(); - shortSleep(); - shortSleep(); - shortSleep(); - - screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); - } - -// @Test - // @ScreenshotTest // todo run without real server -// public void screenshotImages() throws IOException { -// FileDataStorageManager storageManager = new FileDataStorageManager(user, -// targetContext.getContentResolver()); -// -// OCFile newFile = new OCFile("/newFile.txt"); -// newFile.setFileLength(56000); -// newFile.setModificationTimestamp(1522019340); -// newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt"); -// -// File image = getFile("image.jpg"); -// -// assertTrue(new UploadFileRemoteOperation(image.getAbsolutePath(), -// "/image.jpg", -// "image/jpg", -// "10000000").execute(client).isSuccess()); -// -// assertTrue(new RefreshFolderOperation(storageManager.getFileByPath("/"), -// System.currentTimeMillis(), -// false, -// true, -// storageManager, -// user.toPlatformAccount(), -// targetContext -// ).execute(client).isSuccess()); -// -// OCFile existingFile = storageManager.getFileByPath("/image.jpg"); -// -// Intent intent = new Intent(targetContext, ConflictsResolveActivity.class); -// intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile); -// intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile); -// -// ConflictsResolveActivity sut = activityRule.launchActivity(intent); -// -// ConflictsResolveDialog.OnConflictDecisionMadeListener listener = decision -> { -// -// }; -// -// ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile, -// newFile, -// UserAccountManagerImpl -// .fromContext(targetContext) -// .getUser() -// ); -// dialog.showDialog(sut); -// dialog.listener = listener; -// -// getInstrumentation().waitForIdleSync(); -// shortSleep(); -// -// screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); -// } - - @Test - public void cancel() { - returnCode = false; - - OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt", - "/newFile.txt", - user.getAccountName()); - - OCFile existingFile = new OCFile("/newFile.txt"); - existingFile.setFileLength(1024000); - existingFile.setModificationTimestamp(1582019340); - - OCFile newFile = new OCFile("/newFile.txt"); - newFile.setFileLength(56000); - newFile.setModificationTimestamp(1522019340); - newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt"); - - FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver()); - storageManager.saveNewFile(existingFile); - - Intent intent = new Intent(targetContext, ConflictsResolveActivity.class); - intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId()); - - ConflictsResolveActivity sut = activityRule.launchActivity(intent); - - sut.listener = decision -> { - assertEquals(decision, ConflictsResolveDialog.Decision.CANCEL); - returnCode = true; - }; - - getInstrumentation().waitForIdleSync(); - shortSleep(); - - onView(withText("Cancel")).perform(click()); - - assertTrue(returnCode); - } - - @Test - @ScreenshotTest - public void keepExisting() { - returnCode = false; - - OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt", - "/newFile.txt", - user.getAccountName()); - - OCFile existingFile = new OCFile("/newFile.txt"); - existingFile.setFileLength(1024000); - existingFile.setModificationTimestamp(1582019340); - - OCFile newFile = new OCFile("/newFile.txt"); - newFile.setFileLength(56000); - newFile.setModificationTimestamp(1522019340); - newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt"); - - FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver()); - storageManager.saveNewFile(existingFile); - - Intent intent = new Intent(targetContext, ConflictsResolveActivity.class); - intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId()); - - ConflictsResolveActivity sut = activityRule.launchActivity(intent); - - sut.listener = decision -> { - assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_SERVER); - returnCode = true; - }; - - getInstrumentation().waitForIdleSync(); - - onView(withId(R.id.existing_checkbox)).perform(click()); - - DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog"); - screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); - - onView(withText("OK")).perform(click()); - - assertTrue(returnCode); - } - - @Test - @ScreenshotTest - public void keepNew() { - returnCode = false; - - OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt", - "/newFile.txt", - user.getAccountName()); - - OCFile existingFile = new OCFile("/newFile.txt"); - existingFile.setFileLength(1024000); - existingFile.setModificationTimestamp(1582019340); - existingFile.setRemoteId("00000123abc"); - - OCFile newFile = new OCFile("/newFile.txt"); - newFile.setFileLength(56000); - newFile.setModificationTimestamp(1522019340); - newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt"); - - FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver()); - storageManager.saveNewFile(existingFile); - - Intent intent = new Intent(targetContext, ConflictsResolveActivity.class); - intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId()); - - ConflictsResolveActivity sut = activityRule.launchActivity(intent); - - sut.listener = decision -> { - assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_LOCAL); - returnCode = true; - }; - - getInstrumentation().waitForIdleSync(); - - onView(withId(R.id.new_checkbox)).perform(click()); - - DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog"); - screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); - - onView(withText("OK")).perform(click()); - - assertTrue(returnCode); - } - - @Test - @ScreenshotTest - public void keepBoth() { - returnCode = false; - - OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt", - "/newFile.txt", - user.getAccountName()); - - OCFile existingFile = new OCFile("/newFile.txt"); - existingFile.setFileLength(1024000); - existingFile.setModificationTimestamp(1582019340); - - OCFile newFile = new OCFile("/newFile.txt"); - newFile.setFileLength(56000); - newFile.setModificationTimestamp(1522019340); - newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt"); - - FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver()); - storageManager.saveNewFile(existingFile); - - Intent intent = new Intent(targetContext, ConflictsResolveActivity.class); - intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile); - intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId()); - - ConflictsResolveActivity sut = activityRule.launchActivity(intent); - - sut.listener = decision -> { - assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_BOTH); - returnCode = true; - }; - - getInstrumentation().waitForIdleSync(); - - onView(withId(R.id.existing_checkbox)).perform(click()); - onView(withId(R.id.new_checkbox)).perform(click()); - - DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog"); - screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); - - onView(withText("OK")).perform(click()); - - assertTrue(returnCode); - } - - @After - public void after() { - getStorageManager().deleteAllFiles(); - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.kt new file mode 100644 index 0000000..a1c4845 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.kt @@ -0,0 +1,326 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui.activity + +import android.content.Intent +import androidx.annotation.UiThread +import androidx.fragment.app.DialogFragment +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.client.account.UserAccountManagerImpl +import com.nextcloud.utils.extensions.getDecryptedPath +import com.owncloud.android.AbstractIT +import com.owncloud.android.R +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.db.OCUpload +import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Companion.newInstance +import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision +import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.FileStorageUtils +import com.owncloud.android.utils.ScreenshotTest +import junit.framework.TestCase +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class ConflictsResolveActivityIT : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.activity.ConflictsResolveActivityIT" + private var returnCode = false + + @Test + @UiThread + @ScreenshotTest + fun screenshotTextFiles() { + val newFile = OCFile("/newFile.txt").apply { + remoteId = "0001" + fileLength = 56000 + modificationTimestamp = 1522019340 + setStoragePath(FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt") + } + + val existingFile = OCFile("/newFile.txt").apply { + remoteId = "0002" + fileLength = 1024000 + modificationTimestamp = 1582019340 + } + + val storageManager = FileDataStorageManager(user, targetContext.contentResolver) + storageManager.saveNewFile(existingFile) + + val intent = Intent(targetContext, ConflictsResolveActivity::class.java).apply { + putExtra(FileActivity.EXTRA_FILE, newFile) + putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile) + } + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + + val dialog = newInstance( + storageManager.getDecryptedPath(existingFile), + targetContext, + newFile, + existingFile, + UserAccountManagerImpl + .fromContext(targetContext) + .getUser() + ) + dialog.showDialog(sut) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "screenshotTextFiles", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(dialog.requireDialog().window?.decorView, screenShotName) + } + } + } + } + + @Test + @UiThread + fun cancel() { + val newUpload = OCUpload( + FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt", + "/newFile.txt", + user.accountName + ) + + val existingFile = OCFile("/newFile.txt").apply { + fileLength = 1024000 + modificationTimestamp = 1582019340 + } + + val newFile = OCFile("/newFile.txt").apply { + fileLength = 56000 + modificationTimestamp = 1522019340 + setStoragePath(FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt") + } + + EspressoIdlingResource.increment() + FileDataStorageManager(user, targetContext.contentResolver).run { + saveNewFile(existingFile) + } + EspressoIdlingResource.decrement() + + val intent = Intent(targetContext, ConflictsResolveActivity::class.java).apply { + putExtra(FileActivity.EXTRA_FILE, newFile) + putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile) + putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.uploadId) + } + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + returnCode = false + sut.listener = OnConflictDecisionMadeListener { decision: Decision? -> + assertEquals(decision, Decision.CANCEL) + returnCode = true + } + EspressoIdlingResource.decrement() + + onView(ViewMatchers.withText("Cancel")).perform(ViewActions.click()) + TestCase.assertTrue(returnCode) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun keepExisting() { + returnCode = false + + val newUpload = OCUpload( + FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt", + "/newFile.txt", + user.accountName + ) + + val existingFile = OCFile("/newFile.txt").apply { + remoteId = "0001" + fileLength = 1024000 + modificationTimestamp = 1582019340 + } + + val newFile = OCFile("/newFile.txt").apply { + fileLength = 56000 + remoteId = "0002" + modificationTimestamp = 1522019340 + setStoragePath(FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt") + } + + EspressoIdlingResource.increment() + FileDataStorageManager(user, targetContext.contentResolver).run { + saveNewFile(existingFile) + } + EspressoIdlingResource.decrement() + + val intent = Intent(targetContext, ConflictsResolveActivity::class.java).apply { + putExtra(FileActivity.EXTRA_FILE, newFile) + putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile) + putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.uploadId) + } + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.listener = OnConflictDecisionMadeListener { decision: Decision? -> + assertEquals(decision, Decision.KEEP_SERVER) + returnCode = true + } + EspressoIdlingResource.decrement() + + onView(ViewMatchers.withId(R.id.right_checkbox)).perform(ViewActions.click()) + val dialog = sut.supportFragmentManager.findFragmentByTag("conflictDialog") as DialogFragment? + val screenShotName = createName(testClassName + "_" + "keepExisting", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(dialog?.requireDialog()?.window?.decorView, screenShotName) + + onView(ViewMatchers.withText("OK")).perform(ViewActions.click()) + assertTrue(returnCode) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun keepNew() { + returnCode = false + + val newUpload = OCUpload( + FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt", + "/newFile.txt", + user.accountName + ) + + val existingFile = OCFile("/newFile.txt").apply { + fileLength = 1024000 + modificationTimestamp = 1582019340 + remoteId = "00000123abc" + } + + val newFile = OCFile("/newFile.txt").apply { + fileLength = 56000 + modificationTimestamp = 1522019340 + setStoragePath(FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt") + } + + val storageManager = FileDataStorageManager(user, targetContext.contentResolver) + storageManager.saveNewFile(existingFile) + + val intent = Intent(targetContext, ConflictsResolveActivity::class.java) + intent.putExtra(FileActivity.EXTRA_FILE, newFile) + intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile) + intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.uploadId) + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + + sut.listener = OnConflictDecisionMadeListener { decision: Decision? -> + assertEquals(decision, Decision.KEEP_LOCAL) + returnCode = true + } + + EspressoIdlingResource.decrement() + + onView(ViewMatchers.withId(R.id.left_checkbox)).perform(ViewActions.click()) + val dialog = sut.supportFragmentManager.findFragmentByTag("conflictDialog") as DialogFragment? + val screenShotName = createName(testClassName + "_" + "keepNew", "") + screenshotViaName(dialog?.requireDialog()?.window?.decorView, screenShotName) + + onView(ViewMatchers.withText("OK")).perform(ViewActions.click()) + assertTrue(returnCode) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun keepBoth() { + returnCode = false + + val newUpload = OCUpload( + FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt", + "/newFile.txt", + user.accountName + ) + + val existingFile = OCFile("/newFile.txt").apply { + remoteId = "0001" + fileLength = 1024000 + modificationTimestamp = 1582019340 + } + + val newFile = OCFile("/newFile.txt").apply { + fileLength = 56000 + remoteId = "0002" + modificationTimestamp = 1522019340 + setStoragePath(FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt") + } + + val storageManager = FileDataStorageManager(user, targetContext.contentResolver) + storageManager.saveNewFile(existingFile) + + val intent = Intent(targetContext, ConflictsResolveActivity::class.java).apply { + putExtra(FileActivity.EXTRA_FILE, newFile) + putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile) + putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.uploadId) + } + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + + sut.listener = OnConflictDecisionMadeListener { decision: Decision? -> + assertEquals(decision, Decision.KEEP_BOTH) + returnCode = true + } + + EspressoIdlingResource.decrement() + + onView(ViewMatchers.withId(R.id.right_checkbox)).perform(ViewActions.click()) + onView(ViewMatchers.withId(R.id.left_checkbox)).perform(ViewActions.click()) + + onView(ViewMatchers.withId(R.id.left_checkbox)).perform(ViewActions.click()) + val dialog = sut.supportFragmentManager.findFragmentByTag("conflictDialog") as DialogFragment? + val screenShotName = createName(testClassName + "_" + "keepBoth", "") + screenshotViaName(dialog?.requireDialog()?.window?.decorView, screenShotName) + + onView(ViewMatchers.withText("OK")).perform(ViewActions.click()) + assertTrue(returnCode) + } + } + } + } + + @After + override fun after() { + storageManager.deleteAllFiles() + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/ContactsPreferenceActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/ContactsPreferenceActivityIT.kt index cabb10a..9faef5a 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/ContactsPreferenceActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/ContactsPreferenceActivityIT.kt @@ -3,24 +3,42 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity import android.content.Intent -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest +import org.junit.After import org.junit.Assert.assertTrue -import org.junit.Rule +import org.junit.Before import org.junit.Test class ContactsPreferenceActivityIT : AbstractIT() { - @get:Rule - var activityRule = IntentsTestRule(ContactsPreferenceActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.activity.ContactsPreferenceActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun openVCF() { val file = getFile("vcard.vcf") @@ -29,23 +47,34 @@ class ContactsPreferenceActivityIT : AbstractIT() { assertTrue(vcfFile.isDown) - val intent = Intent() - intent.putExtra(ContactsPreferenceActivity.EXTRA_FILE, vcfFile) - intent.putExtra(ContactsPreferenceActivity.EXTRA_USER, user) - val sut = activityRule.launchActivity(intent) + val intent = Intent(targetContext, ContactsPreferenceActivity::class.java).apply { + putExtra(ContactsPreferenceActivity.EXTRA_FILE, vcfFile) + putExtra(ContactsPreferenceActivity.EXTRA_USER, user) + } - shortSleep() - - screenshot(sut) + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "openVCF", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun openContactsPreference() { - val sut = activityRule.launchActivity(null) - - shortSleep() - - screenshot(sut) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "openContactsPreference", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.java b/app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.java deleted file mode 100644 index d4473bc..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.activity; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.net.Uri; -import android.os.Bundle; - -import com.nextcloud.client.account.User; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.account.UserAccountManagerImpl; -import com.nextcloud.test.RetryTestRule; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.lib.common.accounts.AccountUtils; - -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.Matchers.anyOf; -import static org.junit.Assert.assertEquals; - -public class DrawerActivityIT extends AbstractIT { - @Rule public IntentsTestRule activityRule = new IntentsTestRule<>(FileDisplayActivity.class, - true, - false); - - @Rule - public final RetryTestRule retryTestRule = new RetryTestRule(); - - private static Account account1; - private static User user1; - private static Account account2; - private static String account2Name; - private static String account2DisplayName; - - @BeforeClass - public static void beforeClass() { - Bundle arguments = androidx.test.platform.app.InstrumentationRegistry.getArguments(); - Uri baseUrl = Uri.parse(arguments.getString("TEST_SERVER_URL")); - - AccountManager platformAccountManager = AccountManager.get(targetContext); - UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext); - - for (Account account : platformAccountManager.getAccounts()) { - platformAccountManager.removeAccountExplicitly(account); - } - - String loginName = "user1"; - String password = "user1"; - - Account temp = new Account(loginName + "@" + baseUrl, MainApp.getAccountType(targetContext)); - platformAccountManager.addAccountExplicitly(temp, password, null); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION, - Integer.toString(UserAccountManager.ACCOUNT_VERSION)); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0"); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, baseUrl.toString()); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, loginName); // same as userId - - account1 = userAccountManager.getAccountByName(loginName + "@" + baseUrl); - user1 = userAccountManager.getUser(account1.name).orElseThrow(IllegalAccessError::new); - - loginName = "user2"; - password = "user2"; - - temp = new Account(loginName + "@" + baseUrl, MainApp.getAccountType(targetContext)); - platformAccountManager.addAccountExplicitly(temp, password, null); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION, - Integer.toString(UserAccountManager.ACCOUNT_VERSION)); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0"); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, baseUrl.toString()); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, loginName); // same as userId - - account2 = userAccountManager.getAccountByName(loginName + "@" + baseUrl); - account2Name = loginName + "@" + baseUrl; - account2DisplayName = "User Two@" + baseUrl; - } - - @Test - public void switchAccountViaAccountList() { - FileDisplayActivity sut = activityRule.launchActivity(null); - - sut.setUser(user1); - - assertEquals(account1, sut.getUser().get().toPlatformAccount()); - - onView(withId(R.id.switch_account_button)).perform(click()); - - onView(anyOf(withText(account2Name), withText(account2DisplayName))).perform(click()); - - waitForIdleSync(); - - assertEquals(account2, sut.getUser().get().toPlatformAccount()); - - onView(withId(R.id.switch_account_button)).perform(click()); - onView(withText(account1.name)).perform(click()); - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.kt new file mode 100644 index 0000000..6574ff7 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/DrawerActivityIT.kt @@ -0,0 +1,128 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui.activity + +import android.accounts.Account +import android.accounts.AccountManager +import android.net.Uri +import android.view.View +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.platform.app.InstrumentationRegistry +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.account.UserAccountManagerImpl +import com.nextcloud.test.RetryTestRule +import com.owncloud.android.AbstractIT +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.utils.EspressoIdlingResource +import org.hamcrest.Matchers +import org.junit.Assert +import org.junit.BeforeClass +import org.junit.Rule +import org.junit.Test +import java.util.function.Supplier + +class DrawerActivityIT : AbstractIT() { + @Rule + @JvmField + val retryTestRule = RetryTestRule() + + @Test + @UiThread + fun switchAccountViaAccountList() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.setUser(user1) + + Assert.assertEquals(account1, sut.user.get().toPlatformAccount()) + onView(ViewMatchers.withId(R.id.switch_account_button)).perform(ViewActions.click()) + onView( + Matchers.anyOf( + ViewMatchers.withText(account2Name), + ViewMatchers.withText( + account2DisplayName + ) + ) + ).perform(ViewActions.click()) + Assert.assertEquals(account2, sut.user.get().toPlatformAccount()) + EspressoIdlingResource.decrement() + + onView(ViewMatchers.withId(R.id.switch_account_button)).perform(ViewActions.click()) + onView(ViewMatchers.withText(account1?.name)).perform(ViewActions.click()) + } + } + } + } + + companion object { + private var account1: Account? = null + private var user1: User? = null + private var account2: Account? = null + private var account2Name: String? = null + private var account2DisplayName: String? = null + + @JvmStatic + @BeforeClass + fun beforeClass() { + val arguments = InstrumentationRegistry.getArguments() + val baseUrl = Uri.parse(arguments.getString("TEST_SERVER_URL")) + + val platformAccountManager = AccountManager.get(targetContext) + val userAccountManager: UserAccountManager = UserAccountManagerImpl.fromContext(targetContext) + + for (account in platformAccountManager.accounts) { + platformAccountManager.removeAccountExplicitly(account) + } + + var loginName = "user1" + var password = "user1" + + var temp = Account("$loginName@$baseUrl", MainApp.getAccountType(targetContext)) + platformAccountManager.addAccountExplicitly(temp, password, null) + platformAccountManager.setUserData( + temp, + AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION, + UserAccountManager.ACCOUNT_VERSION.toString() + ) + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0") + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, baseUrl.toString()) + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, loginName) // same as userId + + account1 = userAccountManager.getAccountByName("$loginName@$baseUrl") + user1 = userAccountManager.getUser(account1!!.name) + .orElseThrow(Supplier { IllegalAccessError() }) + + loginName = "user2" + password = "user2" + + temp = Account("$loginName@$baseUrl", MainApp.getAccountType(targetContext)) + platformAccountManager.addAccountExplicitly(temp, password, null) + platformAccountManager.setUserData( + temp, + AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION, + UserAccountManager.ACCOUNT_VERSION.toString() + ) + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0") + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, baseUrl.toString()) + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, loginName) // same as userId + + account2 = userAccountManager.getAccountByName("$loginName@$baseUrl") + account2Name = "$loginName@$baseUrl" + account2DisplayName = "User Two@$baseUrl" + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/FileDisplayActivityTest.java b/app/src/androidTest/java/com/owncloud/android/ui/activity/FileDisplayActivityTest.java index 51482ff..c9ec5e0 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/FileDisplayActivityTest.java +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/FileDisplayActivityTest.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Unpublished - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity; diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.java b/app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.java deleted file mode 100644 index fa29777..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2019 Kilian Périsset - * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.activity; - -import android.content.Intent; - -import com.owncloud.android.AbstractIT; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -@RunWith(AndroidJUnit4.class) -//@LargeTest -public class FolderPickerActivityIT extends AbstractIT { - @Rule - public ActivityTestRule activityRule = - new ActivityTestRule<>(FolderPickerActivity.class); - - @Test - public void getActivityFile() { - // Arrange - FolderPickerActivity targetActivity = activityRule.getActivity(); - OCFile origin = new OCFile("/test/file.test"); - origin.setRemotePath("/remotePath/test"); - - // Act - targetActivity.setFile(origin); - OCFile target = targetActivity.getFile(); - - // Assert - Assert.assertEquals(origin, target); - } - - @Test - public void getParentFolder_isNotRootFolder() { - // Arrange - FolderPickerActivity targetActivity = activityRule.getActivity(); - OCFile origin = new OCFile("/test/"); - origin.setFileId(1); - origin.setRemotePath("/test/"); - origin.setStoragePath("/test/"); - origin.setFolder(); - - // Act - targetActivity.setFile(origin); - OCFile target = targetActivity.getCurrentFolder(); - - // Assert - Assert.assertEquals(origin, target); - } - - @Test - public void getParentFolder_isRootFolder() { - // Arrange - FolderPickerActivity targetActivity = activityRule.getActivity(); - OCFile origin = new OCFile("/"); - origin.setFileId(1); - origin.setRemotePath("/"); - origin.setStoragePath("/"); - origin.setFolder(); - - // Act - targetActivity.setFile(origin); - OCFile target = targetActivity.getCurrentFolder(); - - // Assert - Assert.assertEquals(origin, target); - } - - @Test - public void nullFile() { - // Arrange - FolderPickerActivity targetActivity = activityRule.getActivity(); - OCFile rootFolder = targetActivity.getStorageManager().getFileByPath(OCFile.ROOT_PATH); - - // Act - targetActivity.setFile(null); - OCFile target = targetActivity.getCurrentFolder(); - - // Assert - Assert.assertEquals(rootFolder, target); - } - - @Test - public void getParentFolder() { - // Arrange - FolderPickerActivity targetActivity = activityRule.getActivity(); - OCFile origin = new OCFile("/test/file.test"); - origin.setRemotePath("/test/file.test"); - - OCFile target = new OCFile("/test/"); - - // Act - targetActivity.setFile(origin); - - // Assert - Assert.assertEquals(origin, target); - } - - @Test - @ScreenshotTest - public void open() { - FolderPickerActivity sut = activityRule.getActivity(); - OCFile origin = new OCFile("/test/file.txt"); - sut.setFile(origin); - - sut.runOnUiThread(() -> { - sut.findViewById(R.id.folder_picker_btn_copy).requestFocus(); - }); - waitForIdleSync(); - screenshot(sut); - } - - @Test - @ScreenshotTest - public void testMoveOrCopy() { - Intent intent = new Intent(); - FolderPickerActivity targetActivity = activityRule.launchActivity(intent); - - waitForIdleSync(); - screenshot(targetActivity); - } - - @Test - @ScreenshotTest - public void testChooseLocationAction() { - Intent intent = new Intent(); - intent.putExtra(FolderPickerActivity.EXTRA_ACTION, FolderPickerActivity.CHOOSE_LOCATION); - FolderPickerActivity targetActivity = activityRule.launchActivity(intent); - - waitForIdleSync(); - screenshot(targetActivity); - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.kt new file mode 100644 index 0000000..f2e3083 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/FolderPickerActivityIT.kt @@ -0,0 +1,213 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2019 Kilian Périsset + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui.activity + +import android.content.Intent +import android.view.View +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.owncloud.android.AbstractIT +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class FolderPickerActivityIT : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.activity.FolderPickerActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + fun getActivityFile() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val origin = OCFile("/test/file.test").apply { + remotePath = "/remotePath/test" + } + + // Act + sut.file = origin + val target = sut.file + + // Assert + assertEquals(origin, target) + } + } + } + } + + @Test + @UiThread + fun getParentFolder_isNotRootFolder() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + // Arrange + val origin = OCFile("/test/").apply { + fileId = 1 + remotePath = "/test/" + setStoragePath("/test/") + setFolder() + } + + // Act + sut.file = origin + val target = sut.currentFolder + + // Assert + assertEquals(origin, target) + } + } + } + } + + @Test + @UiThread + fun getParentFolder_isRootFolder() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + // Arrange + val origin = OCFile("/").apply { + fileId = 1 + remotePath = "/" + setStoragePath("/") + setFolder() + } + + // Act + sut.file = origin + val target = sut.currentFolder + + // Assert + assertEquals(origin, target) + } + } + } + } + + @Suppress("DEPRECATION") + @Test + @UiThread + fun nullFile() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + // Arrange + val rootFolder = sut.storageManager.getFileByPath(OCFile.ROOT_PATH) + + // Act + sut.file = null + val target = sut.currentFolder + + // Assert + assertEquals(rootFolder, target) + } + } + } + } + + @Test + @UiThread + fun getParentFolder() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + // Arrange + val origin = OCFile("/test/file.test").apply { + remotePath = "/test/file.test" + } + + val target = OCFile("/test/") + + // Act + sut.file = origin + + // Assert + assertEquals(origin, target) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun open() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val origin = OCFile("/test/file.txt") + sut.file = origin + sut.findViewById(R.id.folder_picker_btn_copy).requestFocus() + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun testMoveOrCopy() { + val intent = Intent(targetContext, FolderPickerActivity::class.java) + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "testMoveOrCopy", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun testChooseLocationAction() { + val intent = Intent(targetContext, FolderPickerActivity::class.java).apply { + putExtra(FolderPickerActivity.EXTRA_ACTION, FolderPickerActivity.CHOOSE_LOCATION) + } + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "testChooseLocationAction", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java b/app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java deleted file mode 100644 index c06c7cc..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.activity; - -import android.app.Activity; - -import com.nextcloud.client.account.User; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.lib.common.Quota; -import com.owncloud.android.lib.common.UserInfo; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; - -import java.util.ArrayList; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - -public class ManageAccountsActivityIT extends AbstractIT { - @Rule - public IntentsTestRule activityRule = new IntentsTestRule<>(ManageAccountsActivity.class, - true, - false); - - @Test - @ScreenshotTest - public void open() { - Activity sut = activityRule.launchActivity(null); - - shortSleep(); - - screenshot(sut); - } - - @Test - @ScreenshotTest - public void userInfoDetail() { - ManageAccountsActivity sut = activityRule.launchActivity(null); - - User user = sut.accountManager.getUser(); - - UserInfo userInfo = new UserInfo("test", - true, - "Test User", - "test@nextcloud.com", - "+49 123 456", - "Address 123, Berlin", - "https://www.nextcloud.com", - "https://twitter.com/Nextclouders", - new Quota(), - new ArrayList<>()); - - sut.showUser(user, userInfo); - - shortSleep(); - shortSleep(); - - screenshot(getCurrentActivity()); - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.kt new file mode 100644 index 0000000..dcfe2e6 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.kt @@ -0,0 +1,89 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui.activity + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.common.Quota +import com.owncloud.android.lib.common.UserInfo +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Test + +class ManageAccountsActivityIT : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.activity.ManageAccountsActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + @ScreenshotTest + fun open() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun userInfoDetail() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + + val user = sut.accountManager.user + + val userInfo = UserInfo( + "test", + true, + "Test User", + "test@nextcloud.com", + "+49 123 456", + "Address 123, Berlin", + "https://www.nextcloud.com", + "https://twitter.com/Nextclouders", + Quota(), + ArrayList() + ) + EspressoIdlingResource.decrement() + + sut.showUser(user, userInfo) + + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(getCurrentActivity(), screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt index 28e9690..d75ab28 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/PassCodeActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/PassCodeActivityIT.kt index ff3eb08..9a6befd 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/PassCodeActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/PassCodeActivityIT.kt @@ -1,66 +1,111 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity import android.content.Intent +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity import androidx.test.espresso.Espresso -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class PassCodeActivityIT : AbstractIT() { - @get:Rule - var activityRule = IntentsTestRule(PassCodeActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.activity.PassCodeActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun check() { - val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_CHECK)) + val intent = Intent(targetContext, PassCodeActivity::class.java).apply { + action = PassCodeActivity.ACTION_CHECK + } - waitForIdleSync() + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.binding.txt0.clearFocus() + Espresso.closeSoftKeyboard() + EspressoIdlingResource.decrement() - sut.runOnUiThread { sut.binding.txt0.clearFocus() } - Espresso.closeSoftKeyboard() - shortSleep() - waitForIdleSync() - - screenshot(sut) + val screenShotName = createName(testClassName + "_" + "check", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun request() { - val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_REQUEST_WITH_RESULT)) + val intent = Intent(targetContext, PassCodeActivity::class.java).apply { + action = PassCodeActivity.ACTION_REQUEST_WITH_RESULT + } - waitForIdleSync() + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.binding.txt0.clearFocus() + Espresso.closeSoftKeyboard() + EspressoIdlingResource.decrement() - sut.runOnUiThread { sut.binding.txt0.clearFocus() } - Espresso.closeSoftKeyboard() - shortSleep() - waitForIdleSync() - - screenshot(sut) + val screenShotName = createName(testClassName + "_" + "request", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun delete() { - val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_CHECK_WITH_RESULT)) + val intent = Intent(targetContext, PassCodeActivity::class.java).apply { + action = PassCodeActivity.ACTION_CHECK_WITH_RESULT + } - waitForIdleSync() + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.binding.txt0.clearFocus() + Espresso.closeSoftKeyboard() + EspressoIdlingResource.decrement() - sut.runOnUiThread { sut.binding.txt0.clearFocus() } - Espresso.closeSoftKeyboard() - shortSleep() - waitForIdleSync() - - screenshot(sut) + val screenShotName = createName(testClassName + "_" + "delete", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt index 4b09f50..30ef944 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt @@ -1,28 +1,53 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity -import android.app.Activity -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class ReceiveExternalFilesActivityIT : AbstractIT() { - @get:Rule - val activityRule = IntentsTestRule(ReceiveExternalFilesActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.activity.ReceiveExternalFilesActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun open() { - val sut: Activity = activityRule.launchActivity(null) - screenshot(sut) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "open", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt index 8cc465e..42a9af5 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/UploadFilesActivityIT.kt @@ -1,30 +1,38 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity import android.content.Intent -import androidx.test.espresso.intent.rule.IntentsTestRule -import com.nextcloud.test.GrantStoragePermissionRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.test.GrantStoragePermissionRule.Companion.grant import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.ScreenshotTest import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test +import org.junit.rules.TestRule import java.io.File class UploadFilesActivityIT : AbstractIT() { - @get:Rule - var activityRule = IntentsTestRule(UploadFilesActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.activity.UploadFilesActivityIT" @get:Rule - var permissionRule = GrantStoragePermissionRule.grant() + var storagePermissionRule: TestRule = grant() private val directories = listOf("A", "B", "C", "D") .map { File("${FileStorageUtils.getTemporalPath(account.name)}${File.separator}$it") } @@ -39,60 +47,133 @@ class UploadFilesActivityIT : AbstractIT() { directories.forEach { it.deleteRecursively() } } + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + @Test + @UiThread @ScreenshotTest fun noneSelected() { - val sut: UploadFilesActivity = activityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - sut.runOnUiThread { - sut.fileListFragment.setFiles( - directories + - listOf( - File("1.txt"), - File("2.pdf"), - File("3.mp3") + sut.fileListFragment.setFiles( + directories + + listOf( + File("1.txt"), + File("2.pdf"), + File("3.mp3") + ) ) - ) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "noneSelected", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.fileListFragment.binding?.listRoot, screenShotName) + } + } } - - waitForIdleSync() - longSleep() - - screenshot(sut.fileListFragment.binding.listRoot) } @Test + @UiThread @ScreenshotTest fun localFolderPickerMode() { - val sut: UploadFilesActivity = activityRule.launchActivity( - Intent().apply { - putExtra( - UploadFilesActivity.KEY_LOCAL_FOLDER_PICKER_MODE, - true - ) - putExtra( - UploadFilesActivity.REQUEST_CODE_KEY, - FileDisplayActivity.REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM - ) - } - ) - - sut.runOnUiThread { - sut.fileListFragment.setFiles( - directories + val intent = Intent(targetContext, UploadFilesActivity::class.java).apply { + putExtra( + UploadFilesActivity.KEY_LOCAL_FOLDER_PICKER_MODE, + true + ) + putExtra( + UploadFilesActivity.REQUEST_CODE_KEY, + FileDisplayActivity.REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM ) } - waitForIdleSync() + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - screenshot(sut) + sut.fileListFragment.setFiles( + directories + ) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "localFolderPickerMode", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } - fun fileSelected() { - val sut: UploadFilesActivity = activityRule.launchActivity(null) + @Test + @UiThread + @ScreenshotTest + fun search() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - // TODO select one + sut.fileListFragment.performSearch("1.txt", arrayListOf(), false) + sut.fileListFragment.setFiles( + directories + + listOf( + File("1.txt"), + File("2.pdf"), + File("3.mp3") + ) + ) - screenshot(sut) + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "search", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun selectAll() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + + sut.fileListFragment.setFiles( + listOf( + File("1.txt"), + File("2.pdf"), + File("3.mp3") + ) + ) + + sut.fileListFragment.selectAllFiles(true) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "selectAll", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.fileListFragment.binding?.listRoot, screenShotName) + } + } + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.java b/app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.java deleted file mode 100644 index 46ff6b3..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.activity; - -import android.content.Intent; - -import com.owncloud.android.AbstractIT; -import com.owncloud.android.lib.common.UserInfo; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - -public class UserInfoActivityIT extends AbstractIT { - @Rule - public IntentsTestRule activityRule = new IntentsTestRule<>(UserInfoActivity.class, - true, - false); - - @Test - @ScreenshotTest - public void fullUserInfoDetail() { - final Intent intent = new Intent(targetContext, UserInfoActivity.class); - intent.putExtra(UserInfoActivity.KEY_ACCOUNT, user); - UserInfo userInfo = new UserInfo("test", - true, - "Firstname Familyname", - "oss@rocks.com", - "+49 7613 672 255", - "Awesome Place Av.", - "https://www.nextcloud.com", - "nextclouders", - null, - null - ); - intent.putExtra(UserInfoActivity.KEY_USER_DATA, userInfo); - UserInfoActivity sut = activityRule.launchActivity(intent); - - shortSleep(); - shortSleep(); - - screenshot(sut); - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.kt new file mode 100644 index 0000000..a5ca4b5 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/UserInfoActivityIT.kt @@ -0,0 +1,71 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Andy Scherzinger + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui.activity + +import android.content.Intent +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.common.UserInfo +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Test + +class UserInfoActivityIT : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.activity.UserInfoActivityIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @UiThread + @ScreenshotTest + fun fullUserInfoDetail() { + val intent = Intent(targetContext, UserInfoActivity::class.java).apply { + putExtra(UserInfoActivity.KEY_ACCOUNT, user) + + val userInfo = UserInfo( + "test", + true, + "Firstname Familyname", + "oss@rocks.com", + "+49 7613 672 255", + "Awesome Place Av.", + "https://www.nextcloud.com", + "nextclouders", + null, + null + ) + putExtra(UserInfoActivity.KEY_USER_DATA, userInfo) + } + + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "fullUserInfoDetail", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/adapter/OCFileListAdapterIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/adapter/OCFileListAdapterIT.kt index 00e0924..dec36bb 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/adapter/OCFileListAdapterIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/adapter/OCFileListAdapterIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.adapter diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java b/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java deleted file mode 100644 index c7e6472..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.dialog; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.Intent; -import android.net.http.SslCertificate; -import android.net.http.SslError; -import android.os.Looper; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.SslErrorHandler; -import android.widget.TextView; - -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.gson.Gson; -import com.nextcloud.android.common.ui.color.ColorUtil; -import com.nextcloud.android.common.ui.theme.MaterialSchemes; -import com.nextcloud.android.common.ui.theme.MaterialSchemesImpl; -import com.nextcloud.android.lib.resources.profile.Action; -import com.nextcloud.android.lib.resources.profile.HoverCard; -import com.nextcloud.client.account.RegisteredUser; -import com.nextcloud.client.account.Server; -import com.nextcloud.client.account.User; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.device.DeviceInfo; -import com.nextcloud.client.documentscan.AppScanOptionalFeature; -import com.nextcloud.ui.ChooseAccountDialogFragment; -import com.nextcloud.ui.fileactions.FileActionsBottomSheet; -import com.nextcloud.utils.EditorUtils; -import com.owncloud.android.AbstractIT; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.ArbitraryDataProvider; -import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.common.Creator; -import com.owncloud.android.lib.common.DirectEditing; -import com.owncloud.android.lib.common.Editor; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.accounts.AccountTypeUtils; -import com.owncloud.android.lib.common.accounts.AccountUtils; -import com.owncloud.android.lib.resources.status.CapabilityBooleanType; -import com.owncloud.android.lib.resources.status.OCCapability; -import com.owncloud.android.lib.resources.status.OwnCloudVersion; -import com.owncloud.android.lib.resources.users.Status; -import com.owncloud.android.lib.resources.users.StatusType; -import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.ui.fragment.OCFileListBottomSheetActions; -import com.owncloud.android.ui.fragment.OCFileListBottomSheetDialog; -import com.owncloud.android.ui.fragment.ProfileBottomSheetDialog; -import com.owncloud.android.utils.MimeTypeUtil; -import com.owncloud.android.utils.ScreenshotTest; -import com.owncloud.android.utils.theme.CapabilityUtils; -import com.owncloud.android.utils.theme.MaterialSchemesProvider; -import com.owncloud.android.utils.theme.ViewThemeUtils; - -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mockito; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import androidx.activity.result.contract.ActivityResultContract; -import androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; -import androidx.test.espresso.intent.rule.IntentsTestRule; -import kotlin.Unit; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -public class DialogFragmentIT extends AbstractIT { - - private final String SERVER_URL = "https://nextcloud.localhost"; - - @Rule public IntentsTestRule activityRule = - new IntentsTestRule<>(FileDisplayActivity.class, true, false); - - private FileDisplayActivity getFileDisplayActivity() { - Intent intent = new Intent(targetContext, FileDisplayActivity.class); - return activityRule.launchActivity(intent); - } - - - @After - public void quitLooperIfNeeded() { - if (Looper.myLooper() != null) { - Looper.myLooper().quitSafely(); - } - } - - @Test - @ScreenshotTest - public void testRenameFileDialog() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(new OCFile("/Test/"), - new OCFile("/")); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testLoadingDialog() { - LoadingDialog dialog = LoadingDialog.newInstance("Wait…"); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testConfirmationDialogWithOneAction() { - ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[]{}, R.string.filedetails_sync_file, R.string.common_ok, -1, -1); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testConfirmationDialogWithTwoAction() { - ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[]{}, R.string.filedetails_sync_file, R.string.common_ok, R.string.common_cancel, -1); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testConfirmationDialogWithThreeAction() { - ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[]{}, R.string.filedetails_sync_file, R.string.common_ok, R.string.common_cancel, R.string.common_confirm); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testConfirmationDialogWithThreeActionRTL() { - enableRTL(); - - ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[] { }, -1, R.string.common_ok, R.string.common_cancel, R.string.common_confirm); - showDialog(dialog); - - resetLocale(); - } - - @Test - @ScreenshotTest - public void testRemoveFileDialog() { - RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(new OCFile("/Test.md")); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testRemoveFilesDialog() { - ArrayList toDelete = new ArrayList<>(); - toDelete.add(new OCFile("/Test.md")); - toDelete.add(new OCFile("/Document.odt")); - - RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(toDelete); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testRemoveFolderDialog() { - RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(new OCFile("/Folder/")); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testRemoveFoldersDialog() { - ArrayList toDelete = new ArrayList<>(); - toDelete.add(new OCFile("/Folder/")); - toDelete.add(new OCFile("/Documents/")); - - RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(toDelete); - showDialog(dialog); - } - - @Test - @ScreenshotTest - public void testNewFolderDialog() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - CreateFolderDialogFragment sut = CreateFolderDialogFragment.newInstance(new OCFile("/")); - showDialog(sut); - } - - @Test - @ScreenshotTest - public void testEnforcedPasswordDialog() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - SharePasswordDialogFragment sut = SharePasswordDialogFragment.newInstance(new OCFile("/"), true, false); - showDialog(sut); - } - - @Test - @ScreenshotTest - public void testOptionalPasswordDialog() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - SharePasswordDialogFragment sut = SharePasswordDialogFragment.newInstance(new OCFile("/"), true, true); - showDialog(sut); - } - - @Test - @ScreenshotTest - public void testAccountChooserDialog() throws AccountUtils.AccountNotFoundException { - FileDisplayActivity activity = getFileDisplayActivity(); - UserAccountManager userAccountManager = activity.getUserAccountManager(); - AccountManager accountManager = AccountManager.get(targetContext); - for (Account account : accountManager.getAccountsByType(MainApp.getAccountType(targetContext))) { - accountManager.removeAccountExplicitly(account); - } - - Account newAccount = new Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext)); - accountManager.addAccountExplicitly(newAccount, "password", null); - accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_OC_BASE_URL, SERVER_URL); - accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_USER_ID, "test"); - accountManager.setAuthToken(newAccount, AccountTypeUtils.getAuthTokenTypePass(newAccount.type), "password"); - User newUser = userAccountManager.getUser(newAccount.name).orElseThrow(RuntimeException::new); - userAccountManager.setCurrentOwnCloudAccount(newAccount.name); - - Account newAccount2 = new Account("user1@nextcloud.localhost", MainApp.getAccountType(targetContext)); - accountManager.addAccountExplicitly(newAccount2, "password", null); - accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_OC_BASE_URL, SERVER_URL); - accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_USER_ID, "user1"); - accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_OC_VERSION, "20.0.0"); - accountManager.setAuthToken(newAccount2, AccountTypeUtils.getAuthTokenTypePass(newAccount.type), "password"); - - FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(newUser, - targetContext.getContentResolver()); - - OCCapability capability = new OCCapability(); - capability.setUserStatus(CapabilityBooleanType.TRUE); - capability.setUserStatusSupportsEmoji(CapabilityBooleanType.TRUE); - fileDataStorageManager.saveCapabilities(capability); - - ChooseAccountDialogFragment sut = - ChooseAccountDialogFragment.newInstance(new RegisteredUser(newAccount, - new OwnCloudAccount(newAccount, targetContext), - new Server(URI.create(SERVER_URL), - OwnCloudVersion.nextcloud_20))); - showDialog(activity, sut); - - activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.DND, - "Busy fixing 🐛…", - "", - -1), - targetContext)); - waitForIdleSync(); - shortSleep(); - screenshot(sut, "dnd"); - - activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.ONLINE, - "", - "", - -1), - targetContext)); - waitForIdleSync(); - shortSleep(); - screenshot(sut, "online"); - - activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.ONLINE, - "Let's have some fun", - "🎉", - -1), - targetContext)); - waitForIdleSync(); - shortSleep(); - screenshot(sut, "fun"); - - activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.OFFLINE, "", "", -1), targetContext)); - waitForIdleSync(); - shortSleep(); - screenshot(sut, "offline"); - - activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.AWAY, "Vacation", "🌴", -1), targetContext)); - waitForIdleSync(); - shortSleep(); - screenshot(sut, "away"); - } - - @Test - @ScreenshotTest - public void testAccountChooserDialogWithStatusDisabled() throws AccountUtils.AccountNotFoundException { - AccountManager accountManager = AccountManager.get(targetContext); - for (Account account : accountManager.getAccounts()) { - accountManager.removeAccountExplicitly(account); - } - - Account newAccount = new Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext)); - accountManager.addAccountExplicitly(newAccount, "password", null); - accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_OC_BASE_URL, SERVER_URL); - accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_USER_ID, "test"); - accountManager.setAuthToken(newAccount, AccountTypeUtils.getAuthTokenTypePass(newAccount.type), "password"); - - FileDisplayActivity fda = getFileDisplayActivity(); - UserAccountManager userAccountManager = fda.getUserAccountManager(); - User newUser = userAccountManager.getUser(newAccount.name).get(); - FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(newUser, - targetContext.getContentResolver()); - - OCCapability capability = new OCCapability(); - capability.setUserStatus(CapabilityBooleanType.FALSE); - - fileDataStorageManager.saveCapabilities(capability); - - ChooseAccountDialogFragment sut = - ChooseAccountDialogFragment.newInstance(new RegisteredUser(newAccount, - new OwnCloudAccount(newAccount, targetContext), - new Server(URI.create(SERVER_URL), - OwnCloudVersion.nextcloud_20))); - showDialog(fda, sut); - } - - @Test - @ScreenshotTest - public void testBottomSheet() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - OCFileListBottomSheetActions action = new OCFileListBottomSheetActions() { - - @Override - public void createFolder() { - - } - - @Override - public void uploadFromApp() { - - } - - @Override - public void uploadFiles() { - - } - - @Override - public void newDocument() { - - } - - @Override - public void newSpreadsheet() { - - } - - @Override - public void newPresentation() { - - } - - @Override - public void directCameraUpload() { - - } - - @Override - public void scanDocUpload() { - - } - - @Override - public void showTemplate(Creator creator, String headline) { - - } - - @Override - public void createRichWorkspace() { - - } - }; - - DeviceInfo info = new DeviceInfo(); - OCFile ocFile = new OCFile("/test.md"); - - Intent intent = new Intent(targetContext, FileDisplayActivity.class); - FileDisplayActivity fda = activityRule.launchActivity(intent); - - // add direct editing info - DirectEditing directEditing = new DirectEditing(); - directEditing.getCreators().put("1", new Creator("1", - "text", - "text file", - ".md", - "application/octet-stream", - false)); - - directEditing.getCreators().put("2", new Creator("2", - "md", - "markdown file", - ".md", - "application/octet-stream", - false)); - - directEditing.getEditors().put("text", - new Editor("1", - "Text", - new ArrayList<>(Collections.singletonList(MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN)), - new ArrayList<>(), - false)); - - String json = new Gson().toJson(directEditing); - - new ArbitraryDataProviderImpl(targetContext).storeOrUpdateKeyValue(user.getAccountName(), - ArbitraryDataProvider.DIRECT_EDITING, - json); - - // activate templates - OCCapability capability = fda.getCapabilities(); - capability.setRichDocuments(CapabilityBooleanType.TRUE); - capability.setRichDocumentsDirectEditing(CapabilityBooleanType.TRUE); - capability.setRichDocumentsTemplatesAvailable(CapabilityBooleanType.TRUE); - capability.setAccountName(user.getAccountName()); - - CapabilityUtils.updateCapability(capability); - - AppScanOptionalFeature appScanOptionalFeature = new AppScanOptionalFeature() { - @NonNull - @Override - public ActivityResultContract getScanContract() { - throw new UnsupportedOperationException("Document scan is not available"); - } - }; - - MaterialSchemesProvider materialSchemesProvider = new MaterialSchemesProvider() { - @NonNull - @Override - public MaterialSchemes getMaterialSchemesForUser(@NonNull User user) { - return null; - } - - @NonNull - @Override - public MaterialSchemes getMaterialSchemesForCapability(@NonNull OCCapability capability) { - return null; - } - - @NonNull - @Override - public MaterialSchemes getMaterialSchemesForCurrentUser() { - return new MaterialSchemesImpl(R.color.primary, false); - } - - @NonNull - @Override - public MaterialSchemes getDefaultMaterialSchemes() { - return null; - } - - @NonNull - @Override - public MaterialSchemes getMaterialSchemesForPrimaryBackground() { - return null; - } - }; - - ViewThemeUtils viewThemeUtils = new ViewThemeUtils(materialSchemesProvider.getMaterialSchemesForCurrentUser(), - new ColorUtil(targetContext)); - - EditorUtils editorUtils = new EditorUtils(new ArbitraryDataProviderImpl(targetContext)); - - - OCFileListBottomSheetDialog sut = new OCFileListBottomSheetDialog(fda, - action, - info, - user, - ocFile, - fda.themeUtils, - viewThemeUtils, - editorUtils, - appScanOptionalFeature); - - fda.runOnUiThread(sut::show); - - getInstrumentation().waitForIdleSync(); - shortSleep(); - - sut.getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED); - - getInstrumentation().waitForIdleSync(); - shortSleep(); - - ViewGroup viewGroup = sut.getWindow().findViewById(android.R.id.content); - hideCursors(viewGroup); - - screenshot(Objects.requireNonNull(sut.getWindow()).getDecorView()); - - } - - @Test - @ScreenshotTest - public void testProfileBottomSheet() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - // Fixed values for HoverCard - List actions = new ArrayList<>(); - actions.add(new Action("profile", - "View profile", - "https://dev.nextcloud.com/core/img/actions/profile.svg", - "https://dev.nextcloud.com/index.php/u/christine")); - actions.add(new Action("core", - "christine.scott@nextcloud.com", - "https://dev.nextcloud.com/core/img/actions/mail.svg", - "mailto:christine.scott@nextcloud.com")); - - actions.add(new Action("spreed", - "Talk to Christine", - "https://dev.nextcloud.com/apps/spreed/img/app-dark.svg", - "https://dev.nextcloud.com/apps/spreed/?callUser=christine" - )); - - HoverCard hoverCard = new HoverCard("christine", "Christine Scott", actions); - - // show dialog - Intent intent = new Intent(targetContext, FileDisplayActivity.class); - FileDisplayActivity fda = activityRule.launchActivity(intent); - - ProfileBottomSheetDialog sut = new ProfileBottomSheetDialog(fda, - user, - hoverCard, - fda.viewThemeUtils); - - fda.runOnUiThread(sut::show); - - waitForIdleSync(); - - screenshot(sut.getWindow().getDecorView()); - } - - - @Test - @ScreenshotTest - public void testSslUntrustedCertDialog() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - final SslCertificate certificate = new SslCertificate("foo", "bar", "2022/01/10", "2022/01/30"); - final SslError sslError = new SslError(SslError.SSL_UNTRUSTED, certificate); - - final SslErrorHandler handler = Mockito.mock(SslErrorHandler.class); - - SslUntrustedCertDialog sut = SslUntrustedCertDialog.newInstanceForEmptySslError(sslError, handler); - showDialog(sut); - } - - - @Test - @ScreenshotTest - public void testStoragePermissionDialog() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - StoragePermissionDialogFragment sut = StoragePermissionDialogFragment.Companion.newInstance(false); - showDialog(sut); - } - - @Test - @ScreenshotTest - public void testFileActionsBottomSheet() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - OCFile ocFile = new OCFile("/test.md"); - final FileActionsBottomSheet sut = FileActionsBottomSheet.newInstance(ocFile, false); - showDialog(sut); - } - - private FileDisplayActivity showDialog(DialogFragment dialog) { - Intent intent = new Intent(targetContext, FileDisplayActivity.class); - - FileDisplayActivity sut = activityRule.getActivity(); - - if (sut == null) { - sut = activityRule.launchActivity(intent); - } - - return showDialog(sut, dialog); - } - - private FileDisplayActivity showDialog(FileDisplayActivity sut, DialogFragment dialog) { - dialog.show(sut.getSupportFragmentManager(), ""); - - getInstrumentation().waitForIdleSync(); - shortSleep(); - - ViewGroup viewGroup = dialog.requireDialog().getWindow().findViewById(android.R.id.content); - hideCursors(viewGroup); - - screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); - - return sut; - } - - private void hideCursors(ViewGroup viewGroup) { - for (int i = 0; i < viewGroup.getChildCount(); i++) { - View child = viewGroup.getChildAt(i); - - if (child instanceof ViewGroup) { - hideCursors((ViewGroup) child); - } - - if (child instanceof TextView) { - ((TextView) child).setCursorVisible(false); - } - } - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.kt new file mode 100644 index 0000000..7613b7b --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.kt @@ -0,0 +1,777 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-FileCopyrightText: 2020 Tobias Kaminsky + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui.dialog + +import android.accounts.Account +import android.accounts.AccountManager +import android.app.Dialog +import android.content.Intent +import android.net.http.SslCertificate +import android.net.http.SslError +import android.os.Looper +import android.view.ViewGroup +import android.webkit.SslErrorHandler +import android.widget.TextView +import androidx.activity.result.contract.ActivityResultContract +import androidx.annotation.UiThread +import androidx.fragment.app.DialogFragment +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.nextcloud.android.common.ui.color.ColorUtil +import com.nextcloud.android.lib.resources.profile.Action +import com.nextcloud.android.lib.resources.profile.HoverCard +import com.nextcloud.client.account.RegisteredUser +import com.nextcloud.client.account.Server +import com.nextcloud.client.device.DeviceInfo +import com.nextcloud.client.documentscan.AppScanOptionalFeature +import com.nextcloud.ui.ChooseAccountDialogFragment.Companion.newInstance +import com.nextcloud.ui.SetOnlineStatusBottomSheet +import com.nextcloud.ui.fileactions.FileActionsBottomSheet.Companion.newInstance +import com.nextcloud.utils.EditorUtils +import com.owncloud.android.AbstractIT +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.authentication.EnforcedServer +import com.owncloud.android.datamodel.ArbitraryDataProvider +import com.owncloud.android.datamodel.ArbitraryDataProviderImpl +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.Creator +import com.owncloud.android.lib.common.DirectEditing +import com.owncloud.android.lib.common.Editor +import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.lib.common.accounts.AccountTypeUtils +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.lib.resources.status.CapabilityBooleanType +import com.owncloud.android.lib.resources.status.OCCapability +import com.owncloud.android.lib.resources.status.OwnCloudVersion +import com.owncloud.android.lib.resources.users.Status +import com.owncloud.android.lib.resources.users.StatusType +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.dialog.LoadingDialog.Companion.newInstance +import com.owncloud.android.ui.dialog.RenameFileDialogFragment.Companion.newInstance +import com.owncloud.android.ui.dialog.SharePasswordDialogFragment.Companion.newInstance +import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.Companion.newInstanceForEmptySslError +import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment.Companion.newInstance +import com.owncloud.android.ui.fragment.OCFileListBottomSheetActions +import com.owncloud.android.ui.fragment.OCFileListBottomSheetDialog +import com.owncloud.android.ui.fragment.ProfileBottomSheetDialog +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.MimeTypeUtil +import com.owncloud.android.utils.ScreenshotTest +import com.owncloud.android.utils.theme.CapabilityUtils +import com.owncloud.android.utils.theme.ViewThemeUtils +import io.mockk.mockk +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.net.URI +import java.util.function.Supplier + +@Suppress("TooManyFunctions") +class DialogFragmentIT : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.dialog.DialogFragmentIT" + private val serverUrl = "https://nextcloud.localhost" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun quitLooperIfNeeded() { + Looper.myLooper()?.quitSafely() + } + + @Test + @UiThread + @ScreenshotTest + fun testRenameFileDialog() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + newInstance( + OCFile("/Test/"), + OCFile("/") + ).run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testLoadingDialog() { + newInstance("Wait…").run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testConfirmationDialogWithOneAction() { + ConfirmationDialogFragment.newInstance( + R.string.upload_list_empty_text_auto_upload, + arrayOf(), + R.string.filedetails_sync_file, + R.string.common_ok, + -1, + -1, + -1 + ).run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testConfirmationDialogWithTwoAction() { + ConfirmationDialogFragment.newInstance( + R.string.upload_list_empty_text_auto_upload, + arrayOf(), + R.string.filedetails_sync_file, + R.string.common_ok, + R.string.common_cancel, + -1, + -1 + ).run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testConfirmationDialogWithThreeAction() { + ConfirmationDialogFragment.newInstance( + R.string.upload_list_empty_text_auto_upload, + arrayOf(), + R.string.filedetails_sync_file, + R.string.common_ok, + R.string.common_cancel, + R.string.common_confirm, + -1 + ).run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testConfirmationDialogWithThreeActionRTL() { + enableRTL() + ConfirmationDialogFragment.newInstance( + R.string.upload_list_empty_text_auto_upload, + arrayOf(), + -1, + R.string.common_ok, + R.string.common_cancel, + R.string.common_confirm, + -1 + ).run { + showDialog(this) + resetLocale() + } + } + + @Test + @UiThread + @ScreenshotTest + fun testRemoveFileDialog() { + RemoveFilesDialogFragment.newInstance(OCFile("/Test.md")).run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testRemoveFilesDialog() { + val toDelete = ArrayList().apply { + add(OCFile("/Test.md")) + add(OCFile("/Document.odt")) + } + + val dialog: RemoveFilesDialogFragment = RemoveFilesDialogFragment.newInstance(toDelete) + showDialog(dialog) + } + + @Test + @UiThread + @ScreenshotTest + fun testRemoveFolderDialog() { + val dialog = RemoveFilesDialogFragment.newInstance(OCFile("/Folder/")) + showDialog(dialog) + } + + @Test + @UiThread + @ScreenshotTest + fun testRemoveFoldersDialog() { + val toDelete = ArrayList() + toDelete.add(OCFile("/Folder/")) + toDelete.add(OCFile("/Documents/")) + + val dialog: RemoveFilesDialogFragment = RemoveFilesDialogFragment.newInstance(toDelete) + showDialog(dialog) + } + + @Test + @UiThread + @ScreenshotTest + fun testNewFolderDialog() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + val sut = CreateFolderDialogFragment.newInstance(OCFile("/")) + showDialog(sut) + } + + @Test + @UiThread + @ScreenshotTest + fun testEnforcedPasswordDialog() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + val sut = newInstance(OCFile("/"), true, false) + showDialog(sut) + } + + @Test + @UiThread + @ScreenshotTest + fun testOptionalPasswordDialog() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + val sut = newInstance(OCFile("/"), true, true) + showDialog(sut) + } + + @Test + @UiThread + @ScreenshotTest + fun testAccountChooserDialog() { + val intent = Intent(targetContext, FileDisplayActivity::class.java) + ActivityScenario.launch(intent).use { scenario -> + scenario.onActivity { activity: FileDisplayActivity -> + EspressoIdlingResource.increment() + + val userAccountManager = activity.userAccountManager + val accountManager = AccountManager.get(targetContext) + for (account in accountManager.getAccountsByType(MainApp.getAccountType(targetContext))) { + accountManager.removeAccountExplicitly(account) + } + + val newAccount = Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext)) + accountManager.addAccountExplicitly(newAccount, "password", null) + accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_OC_BASE_URL, serverUrl) + accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_USER_ID, "test") + accountManager.setAuthToken( + newAccount, + AccountTypeUtils.getAuthTokenTypePass(newAccount.type), + "password" + ) + val newUser = userAccountManager.getUser(newAccount.name) + .orElseThrow(Supplier { RuntimeException() }) + userAccountManager.setCurrentOwnCloudAccount(newAccount.name) + + val newAccount2 = Account("user1@nextcloud.localhost", MainApp.getAccountType(targetContext)) + accountManager.addAccountExplicitly(newAccount2, "password", null) + accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_OC_BASE_URL, serverUrl) + accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_USER_ID, "user1") + accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_OC_VERSION, "20.0.0") + accountManager.setAuthToken( + newAccount2, + AccountTypeUtils.getAuthTokenTypePass(newAccount.type), + "password" + ) + + val fileDataStorageManager = FileDataStorageManager( + newUser, + targetContext.contentResolver + ) + + val capability = OCCapability().apply { + userStatus = CapabilityBooleanType.TRUE + userStatusSupportsEmoji = CapabilityBooleanType.TRUE + } + fileDataStorageManager.saveCapabilities(capability) + + EspressoIdlingResource.decrement() + + try { + onIdleSync { + val sut = newInstance( + RegisteredUser( + newAccount, + OwnCloudAccount(newAccount, targetContext), + Server(URI.create(serverUrl), OwnCloudVersion.nextcloud_20) + ) + ) + showDialog(activity, sut) + + sut.setStatus( + Status( + StatusType.DND, + "Busy fixing 🐛…", + "", + -1 + ), + targetContext + ) + screenshot(sut, "dnd") + + sut.setStatus( + Status( + StatusType.ONLINE, + "", + "", + -1 + ), + targetContext + ) + screenshot(sut, "online") + + sut.setStatus( + Status( + StatusType.ONLINE, + "Let's have some fun", + "🎉", + -1 + ), + targetContext + ) + screenshot(sut, "fun") + + sut.setStatus( + Status(StatusType.OFFLINE, "", "", -1), + targetContext + ) + screenshot(sut, "offline") + + sut.setStatus( + Status(StatusType.AWAY, "Vacation", "🌴", -1), + targetContext + ) + screenshot(sut, "away") + } + } catch (e: AccountUtils.AccountNotFoundException) { + throw java.lang.RuntimeException(e) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + @Throws(AccountUtils.AccountNotFoundException::class) + fun testAccountChooserDialogWithStatusDisabled() { + val accountManager = AccountManager.get(targetContext) + for (account in accountManager.accounts) { + accountManager.removeAccountExplicitly(account) + } + + val newAccount = Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext)) + accountManager.addAccountExplicitly(newAccount, "password", null) + accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_OC_BASE_URL, serverUrl) + accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_USER_ID, "test") + accountManager.setAuthToken(newAccount, AccountTypeUtils.getAuthTokenTypePass(newAccount.type), "password") + + launchActivity().use { scenario -> + scenario.onActivity { fda -> + onIdleSync { + EspressoIdlingResource.increment() + val userAccountManager = fda.userAccountManager + val newUser = userAccountManager.getUser(newAccount.name).get() + val fileDataStorageManager = FileDataStorageManager( + newUser, + targetContext.contentResolver + ) + + val capability = OCCapability().apply { + userStatus = CapabilityBooleanType.FALSE + } + + fileDataStorageManager.saveCapabilities(capability) + EspressoIdlingResource.decrement() + + val sut = + newInstance( + RegisteredUser( + newAccount, + OwnCloudAccount(newAccount, targetContext), + Server( + URI.create(serverUrl), + OwnCloudVersion.nextcloud_20 + ) + ) + ) + + onView(isRoot()).check(matches(isDisplayed())) + showDialog(fda, sut) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun testBottomSheet() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + val action: OCFileListBottomSheetActions = object : OCFileListBottomSheetActions { + override fun createFolder() = Unit + override fun uploadFromApp() = Unit + override fun uploadFiles() = Unit + override fun newDocument() = Unit + override fun newSpreadsheet() = Unit + override fun newPresentation() = Unit + override fun directCameraUpload() = Unit + override fun scanDocUpload() = Unit + override fun showTemplate(creator: Creator?, headline: String?) = Unit + override fun createRichWorkspace() = Unit + } + + val info = DeviceInfo() + val ocFile = OCFile("/test.md").apply { + remoteId = "00000001" + } + + val intent = Intent(targetContext, FileDisplayActivity::class.java) + + launchActivity(intent).use { scenario -> + scenario.onActivity { fda -> + onIdleSync { + EspressoIdlingResource.increment() + + // add direct editing info + var directEditing = DirectEditing() + val creators = directEditing.creators.toMutableMap() + val editors = directEditing.editors.toMutableMap() + + creators.put( + "1", + Creator( + "1", + "text", + "text file", + ".md", + "application/octet-stream", + false + ) + ) + creators.put( + "2", + Creator( + "2", + "md", + "markdown file", + ".md", + "application/octet-stream", + false + ) + ) + editors.put( + "text", + Editor( + "1", + "Text", + ArrayList(mutableListOf(MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN)), + ArrayList(), + false + ) + ) + + directEditing = DirectEditing(editors, creators) + val json = Gson().toJson(directEditing) + + ArbitraryDataProviderImpl(targetContext).storeOrUpdateKeyValue( + user.accountName, + ArbitraryDataProvider.DIRECT_EDITING, + json + ) + + // activate templates + val capability = fda.capabilities.apply { + richDocuments = CapabilityBooleanType.TRUE + richDocumentsDirectEditing = CapabilityBooleanType.TRUE + richDocumentsTemplatesAvailable = CapabilityBooleanType.TRUE + accountName = user.accountName + } + CapabilityUtils.updateCapability(capability) + + val appScanOptionalFeature: AppScanOptionalFeature = object : AppScanOptionalFeature() { + override fun getScanContract(): ActivityResultContract = + throw UnsupportedOperationException("Document scan is not available") + } + + val materialSchemesProvider = getMaterialSchemesProvider() + val viewThemeUtils = ViewThemeUtils( + materialSchemesProvider.getMaterialSchemesForCurrentUser(), + ColorUtil(targetContext) + ) + + val editorUtils = EditorUtils(ArbitraryDataProviderImpl(targetContext)) + + val sut = OCFileListBottomSheetDialog( + fda, + action, + info, + user, + ocFile, + fda.themeUtils, + viewThemeUtils, + editorUtils, + appScanOptionalFeature + ) + EspressoIdlingResource.decrement() + + sut.show() + sut.behavior.setState(BottomSheetBehavior.STATE_EXPANDED) + val viewGroup = sut.window?.findViewById(android.R.id.content) ?: return@onIdleSync + hideCursors(viewGroup) + val screenShotName = createName(testClassName + "_" + "testBottomSheet", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.window?.decorView, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun testOnlineStatusBottomSheet() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + // show dialog + val intent = Intent(targetContext, FileDisplayActivity::class.java) + + launchActivity(intent).use { scenario -> + scenario.onActivity { fda -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = SetOnlineStatusBottomSheet( + Status(StatusType.DND, "Focus time", "\uD83E\uDD13", -1) + ) + EspressoIdlingResource.decrement() + sut.show(fda.supportFragmentManager, "set_online_status") + + val screenShotName = createName(testClassName + "_" + "testOnlineStatusBottomSheet", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.view, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun testProfileBottomSheet() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + // Fixed values for HoverCard + val actions: MutableList = ArrayList() + actions.add( + Action( + "profile", + "View profile", + "https://dev.nextcloud.com/core/img/actions/profile.svg", + "https://dev.nextcloud.com/index.php/u/christine" + ) + ) + actions.add( + Action( + "core", + "christine.scott@nextcloud.com", + "https://dev.nextcloud.com/core/img/actions/mail.svg", + "mailto:christine.scott@nextcloud.com" + ) + ) + + actions.add( + Action( + "spreed", + "Talk to Christine", + "https://dev.nextcloud.com/apps/spreed/img/app-dark.svg", + "https://dev.nextcloud.com/apps/spreed/?callUser=christine" + ) + ) + + val hoverCard = HoverCard("christine", "Christine Scott", actions) + + // show dialog + val intent = Intent(targetContext, FileDisplayActivity::class.java) + + launchActivity(intent).use { scenario -> + scenario.onActivity { fda -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = ProfileBottomSheetDialog( + fda, + user, + hoverCard, + fda.viewThemeUtils + ) + EspressoIdlingResource.decrement() + sut.show() + + val screenShotName = createName(testClassName + "_" + "testProfileBottomSheet", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.window?.decorView, screenShotName) + } + } + } + } + + @Test + @UiThread + @ScreenshotTest + fun testSslUntrustedCertDialog() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + val certificate = SslCertificate("foo", "bar", "2022/01/10", "2022/01/30") + val sslError = SslError(SslError.SSL_UNTRUSTED, certificate) + + val handler = mockk(relaxed = true) + + newInstanceForEmptySslError(sslError, handler).run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testStoragePermissionDialog() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + newInstance(false).run { + showDialog(this) + } + } + + @Test + @UiThread + @ScreenshotTest + fun testFileActionsBottomSheet() { + if (Looper.myLooper() == null) { + Looper.prepare() + } + + val ocFile = OCFile("/test.md").apply { + remoteId = "0001" + } + + newInstance(ocFile, false).run { + showDialog(this) + } + } + + private fun showDialog(dialog: DialogFragment) { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + onView(isRoot()).check(matches(isDisplayed())) + showDialog(sut, dialog) + } + } + } + } + + private fun showDialog(sut: FileDisplayActivity, dialog: DialogFragment) { + dialog.show(sut.supportFragmentManager, null) + onIdleSync { + val dialogInstance = waitForDialog(dialog) + ?: throw IllegalStateException("Dialog was not created") + + val viewGroup = dialogInstance.window?.findViewById(android.R.id.content) ?: return@onIdleSync + hideCursors(viewGroup) + + onView(isRoot()).check(matches(isDisplayed())) + screenshot(dialogInstance.window?.decorView) + } + } + + private fun waitForDialog(dialogFragment: DialogFragment, timeoutMs: Long = 5000): Dialog? { + val start = System.currentTimeMillis() + while (System.currentTimeMillis() - start < timeoutMs) { + val dialog = dialogFragment.dialog + if (dialog != null) return dialog + Thread.sleep(100) + } + return null + } + + private fun hideCursors(viewGroup: ViewGroup) { + for (i in 0..().apply { + add(EnforcedServer("name", "url")) + add(EnforcedServer("name2", "url1")) + } + + val s = Gson().toJson(t) + val t2 = Gson().fromJson>( + s, + object : TypeToken?>() { + }.type + ) + + val temp = ArrayList() + for (p in t2) { + temp.add(p.name) + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendFilesDialogTest.kt b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendFilesDialogTest.kt index de52287..9e4ec3d 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendFilesDialogTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendFilesDialogTest.kt @@ -1,26 +1,36 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.dialog +import androidx.annotation.UiThread import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.RecyclerView -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest +import org.junit.After import org.junit.Assert -import org.junit.Rule +import org.junit.Before import org.junit.Test class SendFilesDialogTest : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.dialog.SendFilesDialogTest" + companion object { private val FILES_SAME_TYPE = setOf( OCFile("/1.jpg").apply { @@ -43,52 +53,77 @@ class SendFilesDialogTest : AbstractIT() { ) } - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } - private fun showDialog(files: Set): SendFilesDialog { - val activity = testActivityRule.launchActivity(null) + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } - val fm: FragmentManager = activity.supportFragmentManager - val ft = fm.beginTransaction() - ft.addToBackStack(null) + private fun showDialog(files: Set, onComplete: (SendFilesDialog) -> Unit) { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - val sut = SendFilesDialog.newInstance(files) - sut.show(ft, "TAG_SEND_SHARE_DIALOG") + val fm: FragmentManager = sut.supportFragmentManager + val ft = fm.beginTransaction() + ft.addToBackStack(null) - InstrumentationRegistry.getInstrumentation().waitForIdleSync() - shortSleep() + val dialog = SendFilesDialog.newInstance(files) + dialog.show(ft, "TAG_SEND_SHARE_DIALOG") + onComplete(dialog) - return sut + EspressoIdlingResource.decrement() + } + } + } } @Test + @UiThread + @ScreenshotTest fun showDialog() { - val sut = showDialog(FILES_SAME_TYPE) - val recyclerview: RecyclerView = sut.requireDialog().findViewById(R.id.send_button_recycler_view) - Assert.assertNotNull("Adapter is null", recyclerview.adapter) - Assert.assertNotEquals("Send button list is empty", 0, recyclerview.adapter!!.itemCount) + showDialog(FILES_SAME_TYPE) { sut -> + val recyclerview: RecyclerView = sut.requireDialog().findViewById(R.id.send_button_recycler_view) + Assert.assertNotNull("Adapter is null", recyclerview.adapter) + Assert.assertNotEquals("Send button list is empty", 0, recyclerview.adapter!!.itemCount) + } } @Test + @UiThread @ScreenshotTest fun showDialog_Screenshot() { - val sut = showDialog(FILES_SAME_TYPE) - sut.requireDialog().window?.decorView.let { screenshot(it) } + showDialog(FILES_SAME_TYPE) { sut -> + val screenShotName = createName(testClassName + "_" + "showDialog_Screenshot", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.requireDialog().window?.decorView, screenShotName) + } } @Test + @UiThread + @ScreenshotTest fun showDialogDifferentTypes() { - val sut = showDialog(FILES_MIXED_TYPE) - val recyclerview: RecyclerView = sut.requireDialog().findViewById(R.id.send_button_recycler_view) - Assert.assertNotNull("Adapter is null", recyclerview.adapter) - Assert.assertNotEquals("Send button list is empty", 0, recyclerview.adapter!!.itemCount) + showDialog(FILES_MIXED_TYPE) { sut -> + val recyclerview: RecyclerView = sut.requireDialog().findViewById(R.id.send_button_recycler_view) + Assert.assertNotNull("Adapter is null", recyclerview.adapter) + Assert.assertNotEquals("Send button list is empty", 0, recyclerview.adapter!!.itemCount) + } } @Test + @UiThread @ScreenshotTest fun showDialogDifferentTypes_Screenshot() { - val sut = showDialog(FILES_MIXED_TYPE) - sut.requireDialog().window?.decorView.let { screenshot(it) } + showDialog(FILES_MIXED_TYPE) { sut -> + val screenShotName = createName(testClassName + "_" + "showDialogDifferentTypes_Screenshot", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.requireDialog().window?.decorView, screenShotName) + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendShareDialogTest.kt b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendShareDialogTest.kt index 56d05d6..610aee3 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendShareDialogTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SendShareDialogTest.kt @@ -1,46 +1,55 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.dialog +import androidx.annotation.UiThread import androidx.fragment.app.FragmentManager -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.resources.status.OCCapability +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule import org.junit.Test class SendShareDialogTest : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.dialog.SendShareDialogTest" @Test + @UiThread @ScreenshotTest fun showDialog() { - val activity = testActivityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val fm: FragmentManager = activity.supportFragmentManager + val ft = fm.beginTransaction() + ft.addToBackStack(null) - val fm: FragmentManager = activity.supportFragmentManager - val ft = fm.beginTransaction() - ft.addToBackStack(null) + val file = OCFile("/1.jpg").apply { + mimeType = "image/jpg" + } + EspressoIdlingResource.decrement() - val file = OCFile("/1.jpg").apply { - mimeType = "image/jpg" + val sut = SendShareDialog.newInstance(file, false, OCCapability()) + sut.show(ft, "TAG_SEND_SHARE_DIALOG") + val screenShotName = createName(testClassName + "_" + "showDialog", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.requireDialog().window?.decorView, screenShotName) + } + } } - - val sut = SendShareDialog.newInstance(file, false, OCCapability()) - sut.show(ft, "TAG_SEND_SHARE_DIALOG") - - InstrumentationRegistry.getInstrumentation().waitForIdleSync() - shortSleep() - shortSleep() - sut.requireDialog().window?.decorView.let { screenshot(it) } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragmentIT.kt index 42edfb4..11dd954 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragmentIT.kt @@ -1,79 +1,101 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.dialog -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT +import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class SetupEncryptionDialogFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun showMnemonic() { - val activity = testActivityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = SetupEncryptionDialogFragment.newInstance(user, 0) - val sut = SetupEncryptionDialogFragment.newInstance(user, 0) + sut.show(activity.supportFragmentManager, "1") - sut.show(activity.supportFragmentManager, "1") + val keyWords = arrayListOf( + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "absurd", + "abuse", + "access", + "accident", + "account", + "accuse" + ) + sut.setMnemonic(keyWords) + sut.showMnemonicInfo() + EspressoIdlingResource.decrement() - val keyWords = arrayListOf( - "ability", - "able", - "about", - "above", - "absent", - "absorb", - "abstract", - "absurd", - "abuse", - "access", - "accident", - "account", - "accuse" - ) - - shortSleep() - - runOnUiThread { - sut.setMnemonic(keyWords) - sut.showMnemonicInfo() + val screenShotName = createName(testClassName + "_" + "showMnemonic", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.requireDialog().window?.decorView, screenShotName) + } + } } - - waitForIdleSync() - - screenshot(sut.requireDialog().window!!.decorView) } @Test + @UiThread @ScreenshotTest fun error() { - val activity = testActivityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() - val sut = SetupEncryptionDialogFragment.newInstance(user, 0) + val sut = SetupEncryptionDialogFragment.newInstance(user, 0) + sut.show(activity.supportFragmentManager, "1") + sut.errorSavingKeys() - sut.show(activity.supportFragmentManager, "1") + EspressoIdlingResource.decrement() - shortSleep() - - runOnUiThread { - sut.errorSavingKeys() + val screenShotName = createName(testClassName + "_" + "error", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut.requireDialog().window?.decorView, screenShotName) + } + } } - - shortSleep() - waitForIdleSync() - - screenshot(sut.requireDialog().window!!.decorView) } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.java b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.java deleted file mode 100644 index c82db36..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.dialog; - -import com.owncloud.android.AbstractIT; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; - -import java.util.Objects; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -public class SyncFileNotEnoughSpaceDialogFragmentTest extends AbstractIT { - @Rule public IntentsTestRule activityRule = new IntentsTestRule<>(FileDisplayActivity.class, - true, - false); - - @Test - @ScreenshotTest - public void showNotEnoughSpaceDialogForFolder() { - FileDisplayActivity test = activityRule.launchActivity(null); - OCFile ocFile = new OCFile("/Document/"); - ocFile.setFileLength(5000000); - ocFile.setFolder(); - - SyncFileNotEnoughSpaceDialogFragment dialog = SyncFileNotEnoughSpaceDialogFragment.newInstance(ocFile, 1000); - dialog.show(test.getListOfFilesFragment().getFragmentManager(), "1"); - - getInstrumentation().waitForIdleSync(); - - screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); - } - - @Test - @ScreenshotTest - public void showNotEnoughSpaceDialogForFile() { - FileDisplayActivity test = activityRule.launchActivity(null); - OCFile ocFile = new OCFile("/Video.mp4"); - ocFile.setFileLength(1000000); - - SyncFileNotEnoughSpaceDialogFragment dialog = SyncFileNotEnoughSpaceDialogFragment.newInstance(ocFile, 2000); - dialog.show(test.getListOfFilesFragment().getFragmentManager(), "2"); - - getInstrumentation().waitForIdleSync(); - - screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView()); - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.kt b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.kt new file mode 100644 index 0000000..f4f6246 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragmentTest.kt @@ -0,0 +1,89 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.dialog + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.owncloud.android.AbstractIT +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment.Companion.newInstance +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Test + +class SyncFileNotEnoughSpaceDialogFragmentTest : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @ScreenshotTest + @UiThread + fun showNotEnoughSpaceDialogForFolder() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val ocFile = OCFile("/Document/").apply { + fileLength = 5000000 + setFolder() + } + + onIdleSync { + EspressoIdlingResource.increment() + newInstance(ocFile, 1000).apply { + show(sut.supportFragmentManager, "1") + } + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showNotEnoughSpaceDialogForFolder", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } + + @Test + @ScreenshotTest + @UiThread + fun showNotEnoughSpaceDialogForFile() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val ocFile = OCFile("/Video.mp4").apply { + fileLength = 1000000 + } + + onIdleSync { + EspressoIdlingResource.increment() + newInstance(ocFile, 2000).apply { + show(sut.supportFragmentManager, "2") + } + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showNotEnoughSpaceDialogForFile", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt index c1f9c89..55ac749 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt @@ -1,15 +1,21 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment import android.graphics.BitmapFactory -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.R @@ -17,155 +23,171 @@ import com.owncloud.android.lib.resources.users.StatusType import com.owncloud.android.ui.TextDrawable import com.owncloud.android.utils.BitmapUtils import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class AvatarIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.fragment.AvatarIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun showAvatars() { - val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius) - val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext) - val sut = testActivityRule.launchActivity(null) - val fragment = AvatarTestFragment() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - sut.addFragment(fragment) + val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius) + val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext) + val fragment = AvatarTestFragment() - runOnUiThread { - fragment.addAvatar("Admin", avatarRadius, width, targetContext) - fragment.addAvatar("Test Server Admin", avatarRadius, width, targetContext) - fragment.addAvatar("Cormier Paulette", avatarRadius, width, targetContext) - fragment.addAvatar("winston brent", avatarRadius, width, targetContext) - fragment.addAvatar("Baker James Lorena", avatarRadius, width, targetContext) - fragment.addAvatar("Baker James Lorena", avatarRadius, width, targetContext) - fragment.addAvatar("email@nextcloud.localhost", avatarRadius, width, targetContext) + sut.addFragment(fragment) + fragment.run { + addAvatar("Admin", avatarRadius, width, targetContext) + addAvatar("Test Server Admin", avatarRadius, width, targetContext) + addAvatar("Cormier Paulette", avatarRadius, width, targetContext) + addAvatar("winston brent", avatarRadius, width, targetContext) + addAvatar("Baker James Lorena", avatarRadius, width, targetContext) + addAvatar("Baker James Lorena", avatarRadius, width, targetContext) + addAvatar("email@nextcloud.localhost", avatarRadius, width, targetContext) + } + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showAvatars", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - - shortSleep() - waitForIdleSync() - screenshot(sut) } @Test + @UiThread @ScreenshotTest fun showAvatarsWithStatus() { - val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius) - val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext) - val sut = testActivityRule.launchActivity(null) - val fragment = AvatarTestFragment() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - val paulette = BitmapFactory.decodeFile(getFile("paulette.jpg").absolutePath) - val christine = BitmapFactory.decodeFile(getFile("christine.jpg").absolutePath) - val textBitmap = BitmapUtils.drawableToBitmap(TextDrawable.createNamedAvatar("Admin", avatarRadius)) + val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius) + val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext) + val fragment = AvatarTestFragment() - sut.addFragment(fragment) + val paulette = BitmapFactory.decodeFile(getFile("paulette.jpg").absolutePath) + val christine = BitmapFactory.decodeFile(getFile("christine.jpg").absolutePath) + val textBitmap = BitmapUtils.drawableToBitmap(TextDrawable.createNamedAvatar("Admin", avatarRadius)) - runOnUiThread { - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(paulette, StatusType.ONLINE, "😘", targetContext), - width * 2, - 1, - targetContext - ) + sut.addFragment(fragment) - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "☁️", targetContext), - width * 2, - 1, - targetContext - ) + fragment.run { + addBitmap( + BitmapUtils.createAvatarWithStatus(paulette, StatusType.ONLINE, "😘", targetContext), + width * 2, + 1, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "☁️", targetContext), + width * 2, + 1, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "🌴️", targetContext), + width * 2, + 1, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "", targetContext), + width * 2, + 1, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(paulette, StatusType.DND, "", targetContext), + width * 2, + 1, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(christine, StatusType.AWAY, "", targetContext), + width * 2, + 1, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(paulette, StatusType.OFFLINE, "", targetContext), + width * 2, + 1, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "😘", targetContext), + width, + 2, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "☁️", targetContext), + width, + 2, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "🌴️", targetContext), + width, + 2, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "", targetContext), + width, + 2, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.DND, "", targetContext), + width, + 2, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.AWAY, "", targetContext), + width, + 2, + targetContext + ) + addBitmap( + BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.OFFLINE, "", targetContext), + width, + 2, + targetContext + ) + } + EspressoIdlingResource.decrement() - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "🌴️", targetContext), - width * 2, - 1, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "", targetContext), - width * 2, - 1, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(paulette, StatusType.DND, "", targetContext), - width * 2, - 1, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(christine, StatusType.AWAY, "", targetContext), - width * 2, - 1, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(paulette, StatusType.OFFLINE, "", targetContext), - width * 2, - 1, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "😘", targetContext), - width, - 2, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "☁️", targetContext), - width, - 2, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "🌴️", targetContext), - width, - 2, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "", targetContext), - width, - 2, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.DND, "", targetContext), - width, - 2, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.AWAY, "", targetContext), - width, - 2, - targetContext - ) - - fragment.addBitmap( - BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.OFFLINE, "", targetContext), - width, - 2, - targetContext - ) + val screenShotName = createName(testClassName + "_" + "showAvatarsWithStatus", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - - shortSleep() - waitForIdleSync() - screenshot(sut) } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt index 1999a43..6d21bf1 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarTestFragment.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment @@ -21,8 +21,8 @@ import com.owncloud.android.R import com.owncloud.android.ui.TextDrawable internal class AvatarTestFragment : Fragment() { - lateinit var list1: LinearLayout - lateinit var list2: LinearLayout + private lateinit var list1: LinearLayout + private lateinit var list2: LinearLayout override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view: View = inflater.inflate(R.layout.avatar_fragment, null) @@ -34,7 +34,7 @@ internal class AvatarTestFragment : Fragment() { } fun addAvatar(name: String, avatarRadius: Float, width: Int, targetContext: Context) { - val margin = padding + val margin = PADDING val imageView = ImageView(targetContext) imageView.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadius)) @@ -47,7 +47,7 @@ internal class AvatarTestFragment : Fragment() { } fun addBitmap(bitmap: Bitmap, width: Int, list: Int, targetContext: Context) { - val margin = padding + val margin = PADDING val imageView = ImageView(targetContext) imageView.setImageBitmap(bitmap) @@ -64,6 +64,6 @@ internal class AvatarTestFragment : Fragment() { } companion object { - private const val padding = 10 + private const val PADDING = 10 } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/BackupListFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/BackupListFragmentIT.kt index c10b69d..8f8580f 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/BackupListFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/BackupListFragmentIT.kt @@ -2,99 +2,159 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2021 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment import android.Manifest -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.rule.GrantPermissionRule import com.owncloud.android.AbstractIT import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile import com.owncloud.android.ui.activity.ContactsPreferenceActivity import com.owncloud.android.ui.fragment.contactsbackup.BackupListFragment +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before import org.junit.Rule import org.junit.Test class BackupListFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(ContactsPreferenceActivity::class.java, true, false) - @get:Rule val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CALENDAR) + private val testClassName = "com.owncloud.android.ui.fragment.BackupListFragmentIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + @Test @ScreenshotTest fun showLoading() { - val sut = testActivityRule.launchActivity(null) - val file = OCFile("/") - val transaction = sut.supportFragmentManager.beginTransaction() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val file = OCFile("/") + val transaction = sut.supportFragmentManager.beginTransaction() - transaction.replace(R.id.frame_container, BackupListFragment.newInstance(file, user)) - transaction.commit() + onIdleSync { + EspressoIdlingResource.increment() - waitForIdleSync() - screenshot(sut) + transaction.replace(R.id.frame_container, BackupListFragment.newInstance(file, user)) + transaction.commit() + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showLoading", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test @ScreenshotTest fun showContactList() { - val sut = testActivityRule.launchActivity(null) - val transaction = sut.supportFragmentManager.beginTransaction() - val file = getFile("vcard.vcf") - val ocFile = OCFile("/vcard.vcf") - ocFile.storagePath = file.absolutePath - ocFile.mimeType = "text/vcard" + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val transaction = sut.supportFragmentManager.beginTransaction() + val file = getFile("vcard.vcf") + val ocFile = OCFile("/vcard.vcf").apply { + storagePath = file.absolutePath + mimeType = "text/vcard" + } - transaction.replace(R.id.frame_container, BackupListFragment.newInstance(ocFile, user)) - transaction.commit() + onIdleSync { + EspressoIdlingResource.increment() - waitForIdleSync() - shortSleep() - screenshot(sut) + transaction.replace(R.id.frame_container, BackupListFragment.newInstance(ocFile, user)) + transaction.commit() + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showContactList", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test @ScreenshotTest fun showCalendarList() { - val sut = testActivityRule.launchActivity(null) - val transaction = sut.supportFragmentManager.beginTransaction() - val file = getFile("calendar.ics") - val ocFile = OCFile("/Private calender_2020-09-01_10-45-20.ics.ics") - ocFile.storagePath = file.absolutePath - ocFile.mimeType = "text/calendar" + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val transaction = sut.supportFragmentManager.beginTransaction() + val file = getFile("calendar.ics") + val ocFile = OCFile("/Private calender_2020-09-01_10-45-20.ics.ics").apply { + storagePath = file.absolutePath + mimeType = "text/calendar" + } - transaction.replace(R.id.frame_container, BackupListFragment.newInstance(ocFile, user)) - transaction.commit() + onIdleSync { + EspressoIdlingResource.increment() - waitForIdleSync() - screenshot(sut) + transaction.replace(R.id.frame_container, BackupListFragment.newInstance(ocFile, user)) + transaction.commit() + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showCalendarList", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test @ScreenshotTest fun showCalendarAndContactsList() { - val sut = testActivityRule.launchActivity(null) - val transaction = sut.supportFragmentManager.beginTransaction() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val transaction = sut.supportFragmentManager.beginTransaction() + val calendarFile = getFile("calendar.ics") + val calendarOcFile = OCFile("/Private calender_2020-09-01_10-45-20.ics.ics").apply { + storagePath = calendarFile.absolutePath + mimeType = "text/calendar" + } - val calendarFile = getFile("calendar.ics") - val calendarOcFile = OCFile("/Private calender_2020-09-01_10-45-20.ics") - calendarOcFile.storagePath = calendarFile.absolutePath - calendarOcFile.mimeType = "text/calendar" + val contactFile = getFile("vcard.vcf") + val contactOcFile = OCFile("/vcard.vcf").apply { + storagePath = contactFile.absolutePath + mimeType = "text/vcard" + } - val contactFile = getFile("vcard.vcf") - val contactOcFile = OCFile("/vcard.vcf") - contactOcFile.storagePath = contactFile.absolutePath - contactOcFile.mimeType = "text/vcard" + val files = arrayOf(calendarOcFile, contactOcFile) - val files = arrayOf(calendarOcFile, contactOcFile) - transaction.replace(R.id.frame_container, BackupListFragment.newInstance(files, user)) - transaction.commit() + onIdleSync { + EspressoIdlingResource.increment() - waitForIdleSync() - screenshot(sut) + transaction.replace(R.id.frame_container, BackupListFragment.newInstance(files, user)) + transaction.commit() + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showCalendarAndContactsList", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index 222d9e5..0746143 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -1,14 +1,21 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.nextcloud.ui.ImageDetailFragment import com.owncloud.android.AbstractIT @@ -18,80 +25,111 @@ import com.owncloud.android.lib.resources.activities.model.Activity import com.owncloud.android.lib.resources.activities.model.RichElement import com.owncloud.android.lib.resources.activities.model.RichObject import com.owncloud.android.lib.resources.activities.models.PreviewObject +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test +import java.io.File import java.util.GregorianCalendar class FileDetailFragmentStaticServerIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT" - var file = getFile("gps.jpg") - val oCFile = OCFile("/").apply { + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + private var file: File = getFile("gps.jpg") + private val oCFile: OCFile = OCFile("/").apply { storagePath = file.absolutePath fileId = 12 fileDataStorageManager.saveFile(this) } @Test + @UiThread @ScreenshotTest fun showFileDetailActivitiesFragment() { - val sut = testActivityRule.launchActivity(null) - sut.addFragment(FileDetailActivitiesFragment.newInstance(oCFile, user)) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.addFragment(FileDetailActivitiesFragment.newInstance(oCFile, user)) + EspressoIdlingResource.decrement() - waitForIdleSync() - shortSleep() - shortSleep() - screenshot(sut) + val screenShotName = createName(testClassName + "_" + "showFileDetailActivitiesFragment", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun showFileDetailSharingFragment() { - val sut = testActivityRule.launchActivity(null) - sut.addFragment(FileDetailSharingFragment.newInstance(oCFile, user)) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + sut.addFragment(FileDetailSharingFragment.newInstance(oCFile, user)) + EspressoIdlingResource.decrement() - waitForIdleSync() - shortSleep() - shortSleep() - screenshot(sut) + val screenShotName = createName(testClassName + "_" + "showFileDetailSharingFragment", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun showFileDetailDetailsFragment() { - val activity = testActivityRule.launchActivity(null) - val sut = ImageDetailFragment.newInstance(oCFile, user) - activity.addFragment(sut) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val fragment = ImageDetailFragment.newInstance(oCFile, user).apply { + hideMap() + } + sut.addFragment(fragment) + EspressoIdlingResource.decrement() - shortSleep() - shortSleep() - waitForIdleSync() - - activity.runOnUiThread { - sut.hideMap() + val screenShotName = createName(testClassName + "_" + "showFileDetailDetailsFragment", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - - screenshot(activity) } @Test + @UiThread @ScreenshotTest @Suppress("MagicNumber") fun showDetailsActivities() { - val date = GregorianCalendar() - date.set(2005, 4, 17, 10, 35, 30) // random date + val date = GregorianCalendar().apply { + set(2005, 4, 17, 10, 35, 30) + } - val richObjectList: ArrayList = ArrayList() - richObjectList.add(RichObject("file", "abc", "text.txt", "/text.txt", "link", "tag")) - richObjectList.add(RichObject("file", "1", "text.txt", "/text.txt", "link", "tag")) + val richObjectList = ArrayList().apply { + add(RichObject("file", "abc", "text.txt", "/text.txt", "link", "tag")) + add(RichObject("file", "1", "text.txt", "/text.txt", "link", "tag")) + } - val previewObjectList1: ArrayList = ArrayList() - previewObjectList1.add(PreviewObject(1, "source", "link", true, "text/plain", "view", "text.txt")) - - val richObjectList2: ArrayList = ArrayList() - richObjectList2.add(RichObject("user", "admin", "Admin", "", "", "")) + val previewObjectList1 = ArrayList().apply { + add(PreviewObject(1, "source", "link", true, "text/plain", "view", "text.txt")) + } val activities = mutableListOf( Activity( @@ -132,67 +170,85 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { ) ) - val sut = FileDetailFragment.newInstance(oCFile, user, 0) - testActivityRule.launchActivity(null).apply { - addFragment(sut) - waitForIdleSync() - runOnUiThread { - sut.fileDetailActivitiesFragment.populateList(activities as List?, true) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val fragment = FileDetailFragment.newInstance(oCFile, user, 0) + sut.addFragment(fragment) + fragment.fileDetailActivitiesFragment.populateList(activities as List?, true) + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showDetailsActivities", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } } - longSleep() - screenshot(sut.fileDetailActivitiesFragment.binding.swipeContainingList) } } - // @Test - // @ScreenshotTest - fun showDetailsActivitiesNone() { - val activity = testActivityRule.launchActivity(null) - val sut = FileDetailFragment.newInstance(oCFile, user, 0) - activity.addFragment(sut) - - waitForIdleSync() - - activity.runOnUiThread { - sut.fileDetailActivitiesFragment.populateList(emptyList(), true) - } - - shortSleep() - shortSleep() - screenshot(sut.fileDetailActivitiesFragment.binding.list) - } - @Test + @UiThread + @ScreenshotTest + fun showDetailsActivitiesNone() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val fragment = FileDetailFragment.newInstance(oCFile, user, 0) + sut.addFragment(fragment) + fragment.fileDetailActivitiesFragment.populateList(emptyList(), true) + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showDetailsActivitiesNone", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } + + @Test + @UiThread @ScreenshotTest fun showDetailsActivitiesError() { - val activity = testActivityRule.launchActivity(null) - val sut = FileDetailFragment.newInstance(oCFile, user, 0) - activity.addFragment(sut) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val fragment = FileDetailFragment.newInstance(oCFile, user, 0) + sut.addFragment(fragment) + fragment.fileDetailActivitiesFragment.disableLoadingActivities() + fragment.fileDetailActivitiesFragment.setErrorContent( + targetContext.resources.getString(R.string.file_detail_activity_error) + ) + EspressoIdlingResource.decrement() - waitForIdleSync() - - activity.runOnUiThread { - sut.fileDetailActivitiesFragment.disableLoadingActivities() - sut - .fileDetailActivitiesFragment - .setErrorContent(targetContext.resources.getString(R.string.file_detail_activity_error)) + val screenShotName = createName(testClassName + "_" + "showDetailsActivitiesError", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - - shortSleep() - shortSleep() - screenshot(sut.fileDetailActivitiesFragment.binding.emptyList.emptyListView) } @Test + @UiThread @ScreenshotTest fun showDetailsSharing() { - val sut = testActivityRule.launchActivity(null) - sut.addFragment(FileDetailFragment.newInstance(oCFile, user, 1)) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val fragment = FileDetailFragment.newInstance(oCFile, user, 1) + sut.addFragment(fragment) + EspressoIdlingResource.decrement() - waitForIdleSync() - - shortSleep() - shortSleep() - screenshot(sut) + val screenShotName = createName(testClassName + "_" + "showDetailsSharing", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt index 3b794bc..7fa76a8 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt @@ -4,24 +4,28 @@ * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2021 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment import android.view.View +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.accessibility.AccessibilityChecks import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isChecked import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isNotChecked +import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withText import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultBaseUtils.matchesCheckNames import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.nextcloud.android.lib.resources.files.FileDownloadLimit import com.nextcloud.test.RetryTestRule import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT @@ -37,55 +41,68 @@ import com.owncloud.android.lib.resources.shares.OCShare.Companion.READ_PERMISSI import com.owncloud.android.lib.resources.shares.OCShare.Companion.SHARE_PERMISSION_FLAG import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.ui.activity.FileDisplayActivity -import com.owncloud.android.ui.fragment.util.SharingMenuHelper +import com.owncloud.android.ui.fragment.util.SharePermissionManager +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.anyOf import org.hamcrest.CoreMatchers.`is` import org.hamcrest.CoreMatchers.not import org.junit.After -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue +import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test @Suppress("TooManyFunctions") class FileDetailSharingFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT" @get:Rule val retryRule = RetryTestRule() lateinit var file: OCFile lateinit var folder: OCFile - lateinit var activity: TestActivity + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Before fun before() { - activity = testActivityRule.launchActivity(null) - file = OCFile("/test.md").apply { - remoteId = "00000001" - parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId - permissions = OCFile.PERMISSION_CAN_RESHARE - fileDataStorageManager.saveFile(this) - } + launchActivity().use { scenario -> + scenario.onActivity { activity -> + file = OCFile("/test.md").apply { + remoteId = "00000001" + parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId + permissions = OCFile.PERMISSION_CAN_RESHARE + fileDataStorageManager.saveFile(this) + } - folder = OCFile("/test").apply { - setFolder() - parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId - permissions = OCFile.PERMISSION_CAN_RESHARE + folder = OCFile("/test").apply { + setFolder() + parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId + permissions = OCFile.PERMISSION_CAN_RESHARE + } + } } } @Test + @UiThread @ScreenshotTest fun listSharesFileNone() { show(file) } @Test + @UiThread @ScreenshotTest fun listSharesFileResharingNotAllowed() { file.permissions = "" @@ -93,127 +110,197 @@ class FileDetailSharingFragmentIT : AbstractIT() { show(file) } + @Test + @UiThread + @ScreenshotTest + fun listSharesDownloadLimit() { + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.PUBLIC_LINK + token = "AAAAAAAAAAAAAAA" + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 2 + shareType = ShareType.PUBLIC_LINK + token = "BBBBBBBBBBBBBBB" + fileDownloadLimit = FileDownloadLimit("BBBBBBBBBBBBBBB", 0, 0) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.PUBLIC_LINK + token = "CCCCCCCCCCCCCCC" + fileDownloadLimit = FileDownloadLimit("CCCCCCCCCCCCCCC", 10, 0) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + token = "DDDDDDDDDDDDDDD" + fileDownloadLimit = FileDownloadLimit("DDDDDDDDDDDDDDD", 10, 5) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 5 + shareType = ShareType.PUBLIC_LINK + token = "FFFFFFFFFFFFFFF" + fileDownloadLimit = FileDownloadLimit("FFFFFFFFFFFFFFF", 10, 10) + activity.storageManager.saveShare(this) + } + EspressoIdlingResource.decrement() + + show(file) + } + } + } + } + /** * Use same values as {@link OCFileListFragmentStaticServerIT showSharedFiles } */ @Test + @UiThread @ScreenshotTest @Suppress("MagicNumber") fun listSharesFileAllShareTypes() { - OCShare(file.decryptedRemotePath).apply { - remoteId = 1 - shareType = ShareType.USER - sharedWithDisplayName = "Admin" - permissions = MAXIMUM_PERMISSIONS_FOR_FILE - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.USER + sharedWithDisplayName = "Admin" + permissions = MAXIMUM_PERMISSIONS_FOR_FILE + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 2 - shareType = ShareType.GROUP - sharedWithDisplayName = "Group" - permissions = MAXIMUM_PERMISSIONS_FOR_FILE - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 2 + shareType = ShareType.GROUP + sharedWithDisplayName = "Group" + permissions = MAXIMUM_PERMISSIONS_FOR_FILE + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 3 - shareType = ShareType.EMAIL - sharedWithDisplayName = "admin@nextcloud.localhost" - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.EMAIL + sharedWithDisplayName = "admin@nextcloud.localhost" + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 4 - shareType = ShareType.PUBLIC_LINK - label = "Customer" - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + label = "Customer" + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 5 - shareType = ShareType.PUBLIC_LINK - label = "Colleagues" - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 5 + shareType = ShareType.PUBLIC_LINK + label = "Colleagues" + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 6 - shareType = ShareType.FEDERATED - sharedWithDisplayName = "admin@nextcloud.localhost" - permissions = OCShare.FEDERATED_PERMISSIONS_FOR_FILE - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 6 + shareType = ShareType.FEDERATED + sharedWithDisplayName = "admin@nextcloud.localhost" + permissions = OCShare.FEDERATED_PERMISSIONS_FOR_FILE + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 7 - shareType = ShareType.CIRCLE - sharedWithDisplayName = "Personal team" - permissions = SHARE_PERMISSION_FLAG - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 7 + shareType = ShareType.CIRCLE + sharedWithDisplayName = "Personal team" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 8 - shareType = ShareType.CIRCLE - sharedWithDisplayName = "Public team" - permissions = SHARE_PERMISSION_FLAG - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 8 + shareType = ShareType.CIRCLE + sharedWithDisplayName = "Public team" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 9 - shareType = ShareType.CIRCLE - sharedWithDisplayName = "Closed team" - permissions = SHARE_PERMISSION_FLAG - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 9 + shareType = ShareType.CIRCLE + sharedWithDisplayName = "Closed team" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 10 - shareType = ShareType.CIRCLE - sharedWithDisplayName = "Secret team" - permissions = SHARE_PERMISSION_FLAG - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 10 + shareType = ShareType.CIRCLE + sharedWithDisplayName = "Secret team" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 11 - shareType = ShareType.ROOM - sharedWithDisplayName = "Admin" - permissions = SHARE_PERMISSION_FLAG - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 11 + shareType = ShareType.ROOM + sharedWithDisplayName = "Admin" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } - OCShare(file.decryptedRemotePath).apply { - remoteId = 12 - shareType = ShareType.ROOM - sharedWithDisplayName = "Meeting" - permissions = SHARE_PERMISSION_FLAG - userId = getUserId(user) - activity.storageManager.saveShare(this) - } + OCShare(file.decryptedRemotePath).apply { + remoteId = 12 + shareType = ShareType.ROOM + sharedWithDisplayName = "Meeting" + permissions = SHARE_PERMISSION_FLAG + userId = getUserId(user) + activity.storageManager.saveShare(this) + } + EspressoIdlingResource.decrement() - show(file) + show(file) + } + } + } } private fun show(file: OCFile) { - val fragment = FileDetailSharingFragment.newInstance(file, user) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() + val fragment = FileDetailSharingFragment.newInstance(file, user) + sut.addFragment(fragment) + EspressoIdlingResource.decrement() - activity.addFragment(fragment) - - waitForIdleSync() - - screenshot(activity) + val screenShotName = createName(testClassName + "_" + "show", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } // public link and email are handled the same way @@ -221,96 +308,101 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun publicLinkOptionMenuFolderAdvancePermission() { - val sut = FileDetailSharingFragment.newInstance(file, user) - activity.addFragment(sut) - setupSecondaryFragment() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + sut.refreshCapabilitiesFromDB() - val publicShare = OCShare().apply { - isFolder = true - shareType = ShareType.PUBLIC_LINK - permissions = 17 + val publicShare = OCShare().apply { + isFolder = true + shareType = ShareType.PUBLIC_LINK + permissions = 17 + } + + EspressoIdlingResource.decrement() + activity.runOnUiThread { sut.showSharingMenuActionSheet(publicShare) } + + // check if items are visible + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) + + // click event + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) + + // validate view shown on screen + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed())) + + // read-only + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isNotChecked())) + goBack() + + // upload and editing + publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isNotChecked())) + goBack() + + // file request + publicShare.permissions = 4 + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isChecked())) + goBack() + + // password protection + publicShare.shareWith = "someValue" + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked())) + goBack() + + publicShare.shareWith = "" + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked())) + goBack() + + // hide download + publicShare.isHideFileDownload = true + publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked())) + goBack() + + publicShare.isHideFileDownload = false + openAdvancedPermissions(sut, publicShare) + onView( + ViewMatchers.withId(R.id.share_process_hide_download_checkbox) + ).check(matches(isNotChecked())) + goBack() + + publicShare.expirationDate = 1582019340000 + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) + goBack() + + publicShare.expirationDate = 0 + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) + } + } } - - activity.runOnUiThread { sut.showSharingMenuActionSheet(publicShare) } - shortSleep() - waitForIdleSync() - - // check if items are visible - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed())) - - // click event - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) - - // validate view shown on screen - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(not(isDisplayed()))) - - // read-only - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked())) - goBack() - - // upload and editing - publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked())) - goBack() - - // file drop - publicShare.permissions = 4 - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isChecked())) - goBack() - - // password protection - publicShare.shareWith = "someValue" - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked())) - goBack() - - publicShare.shareWith = "" - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked())) - goBack() - - // hide download - publicShare.isHideFileDownload = true - publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked())) - goBack() - - publicShare.isHideFileDownload = false - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isNotChecked())) - goBack() - - publicShare.expirationDate = 1582019340000 - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) - goBack() - - publicShare.expirationDate = 0 - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) } // public link and email are handled the same way @@ -318,32 +410,43 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun publicLinkOptionMenuFolderSendNewEmail() { - val sut = FileDetailSharingFragment.newInstance(file, user) - activity.addFragment(sut) - setupSecondaryFragment() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + sut.refreshCapabilitiesFromDB() + EspressoIdlingResource.decrement() - val publicShare = OCShare().apply { - isFolder = true - shareType = ShareType.PUBLIC_LINK - permissions = 17 + val publicShare = OCShare().apply { + isFolder = true + shareType = ShareType.PUBLIC_LINK + permissions = 17 + } + + verifySendNewEmail(sut, publicShare) + } + } } - - verifySendNewEmail(sut, publicShare) } private fun setupSecondaryFragment() { - val parentFolder = OCFile("/") - val secondary = FileDetailFragment.newInstance(file, parentFolder, user) - activity.addSecondaryFragment(secondary, FileDisplayActivity.TAG_LIST_OF_FILES) - activity.addView( - FloatingActionButton(activity).apply { - // needed for some reason - visibility = View.GONE - id = R.id.fab_main + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val parentFolder = OCFile("/") + val secondary = FileDetailFragment.newInstance(file, parentFolder, user) + activity.addSecondaryFragment(secondary, FileDisplayActivity.TAG_LIST_OF_FILES) + activity.addView( + FloatingActionButton(activity).apply { + // needed for some reason + visibility = View.GONE + id = R.id.fab_main + } + ) } - ) + } } // public link and email are handled the same way @@ -351,87 +454,96 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun publicLinkOptionMenuFileAdvancePermission() { - val sut = FileDetailSharingFragment.newInstance(file, user) - activity.addFragment(sut) - setupSecondaryFragment() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) - val publicShare = OCShare().apply { - isFolder = false - shareType = ShareType.PUBLIC_LINK - permissions = 17 + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + sut.refreshCapabilitiesFromDB() + EspressoIdlingResource.decrement() + + val publicShare = OCShare().apply { + isFolder = false + shareType = ShareType.PUBLIC_LINK + permissions = 17 + } + activity.handler.post { sut.showSharingMenuActionSheet(publicShare) } + + // check if items are visible + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) + + // click event + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) + + // validate view shown on screen + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isDisplayed())) + onView( + ViewMatchers.withId(R.id.file_request_radio_button) + ).check(matches(not(isDisplayed()))) + onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed())) + + // read-only + publicShare.permissions = 17 // from server + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isNotChecked())) + goBack() + + // editing + publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isChecked())) + goBack() + + // hide download + publicShare.isHideFileDownload = true + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked())) + goBack() + + publicShare.isHideFileDownload = false + openAdvancedPermissions(sut, publicShare) + onView( + ViewMatchers.withId(R.id.share_process_hide_download_checkbox) + ).check(matches(isNotChecked())) + goBack() + + // password protection + publicShare.isPasswordProtected = true + publicShare.shareWith = "someValue" + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked())) + goBack() + + publicShare.isPasswordProtected = false + publicShare.shareWith = "" + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked())) + goBack() + + // expires + publicShare.expirationDate = 1582019340 + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) + goBack() + + publicShare.expirationDate = 0 + openAdvancedPermissions(sut, publicShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) + } + } } - activity.handler.post { sut.showSharingMenuActionSheet(publicShare) } - waitForIdleSync() - - // check if items are visible - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed())) - - // click event - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) - - // validate view shown on screen - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(not(isDisplayed()))) - - // read-only - publicShare.permissions = 17 // from server - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked())) - goBack() - - // editing - publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked())) - goBack() - - // hide download - publicShare.isHideFileDownload = true - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked())) - goBack() - - publicShare.isHideFileDownload = false - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isNotChecked())) - goBack() - - // password protection - publicShare.isPasswordProtected = true - publicShare.shareWith = "someValue" - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked())) - goBack() - - publicShare.isPasswordProtected = false - publicShare.shareWith = "" - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked())) - goBack() - - // expires - publicShare.expirationDate = 1582019340 - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) - goBack() - - publicShare.expirationDate = 0 - openAdvancedPermissions(sut, publicShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) } // public link and email are handled the same way @@ -439,19 +551,26 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun publicLinkOptionMenuFileSendNewEmail() { - val sut = FileDetailSharingFragment.newInstance(file, user) - activity.addFragment(sut) - setupSecondaryFragment() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + sut.refreshCapabilitiesFromDB() + EspressoIdlingResource.decrement() - val publicShare = OCShare().apply { - isFolder = false - shareType = ShareType.PUBLIC_LINK - permissions = 17 + val publicShare = OCShare().apply { + isFolder = false + shareType = ShareType.PUBLIC_LINK + permissions = 17 + } + + verifySendNewEmail(sut, publicShare) + } + } } - - verifySendNewEmail(sut, publicShare) } // also applies for @@ -463,79 +582,78 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun userOptionMenuFileAdvancePermission() { - val sut = FileDetailSharingFragment.newInstance(file, user) - suppressFDFAccessibilityChecks() - activity.addFragment(sut) - setupSecondaryFragment() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + suppressFDFAccessibilityChecks() + activity.addFragment(sut) - val userShare = OCShare().apply { - isFolder = false - shareType = ShareType.USER - permissions = 17 + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + sut.refreshCapabilitiesFromDB() + EspressoIdlingResource.decrement() + + val userShare = OCShare().apply { + isFolder = false + shareType = ShareType.USER + permissions = 17 + } + + activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) } + + // check if items are visible + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed()))) + onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) + + // click event + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) + + // validate view shown on screen + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isDisplayed())) + onView( + ViewMatchers.withId(R.id.file_request_radio_button) + ).check(matches(not(isDisplayed()))) + onView( + ViewMatchers.withId(R.id.share_process_hide_download_checkbox) + ).check(matches(not(isDisplayed()))) + onView( + ViewMatchers.withId(R.id.share_process_set_password_switch) + ).check(matches(not(isDisplayed()))) + onView( + ViewMatchers.withId(R.id.share_process_change_name_switch) + ).check(matches(not(isDisplayed()))) + + // read-only + userShare.permissions = 17 // from server + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isNotChecked())) + goBack() + + // editing + userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server + openAdvancedPermissions(sut, userShare) + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isChecked())) + goBack() + + // set expiration date + userShare.expirationDate = 1582019340000 + openAdvancedPermissions(sut, userShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) + goBack() + + userShare.expirationDate = 0 + openAdvancedPermissions(sut, userShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) + } + } } - - activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) } - shortSleep() - waitForIdleSync() - - // check if items are visible - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed()))) - - // click event - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) - shortSleep() - waitForIdleSync() - - // validate view shown on screen - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isDisplayed())) - - // read-only - userShare.permissions = 17 // from server - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked())) - goBack() - - // editing - userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked())) - goBack() - - // allow reshare - userShare.permissions = 1 // from server - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isNotChecked())) - goBack() - - userShare.permissions = 17 // from server - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isChecked())) - goBack() - - // set expiration date - userShare.expirationDate = 1582019340000 - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) - goBack() - - userShare.expirationDate = 0 - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) } private fun suppressFDFAccessibilityChecks() { @@ -564,19 +682,28 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun userOptionMenuFileSendNewEmail() { - val sut = FileDetailSharingFragment.newInstance(file, user) - activity.addFragment(sut) - setupSecondaryFragment() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) - val userShare = OCShare().apply { - isFolder = false - shareType = ShareType.USER - permissions = 17 + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + sut.refreshCapabilitiesFromDB() + EspressoIdlingResource.decrement() + + val userShare = OCShare().apply { + remoteId = 1001L + isFolder = false + shareType = ShareType.USER + permissions = 17 + } + + verifySendNewEmail(sut, userShare) + } + } } - - verifySendNewEmail(sut, userShare) } // also applies for @@ -588,112 +715,118 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun userOptionMenuFolderAdvancePermission() { - val sut = FileDetailSharingFragment.newInstance(file, user) - activity.addFragment(sut) - setupSecondaryFragment() - suppressFDFAccessibilityChecks() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) - val userShare = OCShare().apply { - isFolder = true - shareType = ShareType.USER - permissions = 17 + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + suppressFDFAccessibilityChecks() + sut.refreshCapabilitiesFromDB() + EspressoIdlingResource.decrement() + + val userShare = OCShare().apply { + isFolder = true + shareType = ShareType.USER + permissions = 17 + } + + activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) } + + // check if items are visible + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed()))) + onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) + + // click event + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) + + // validate view shown on screen + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isDisplayed())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isDisplayed())) + onView( + ViewMatchers.withId(R.id.share_process_hide_download_checkbox) + ).check(matches(not(isDisplayed()))) + onView( + ViewMatchers.withId(R.id.share_process_set_password_switch) + ).check(matches(not(isDisplayed()))) + onView( + ViewMatchers.withId(R.id.share_process_change_name_switch) + ).check(matches(not(isDisplayed()))) + + // read-only + userShare.permissions = 17 // from server + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isNotChecked())) + goBack() + + // allow upload & editing + userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER // from server + openAdvancedPermissions(sut, userShare) + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isNotChecked())) + goBack() + + // file request + userShare.permissions = 4 + openAdvancedPermissions(sut, userShare) + onView(ViewMatchers.withId(R.id.view_only_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.can_edit_radio_button)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.file_request_radio_button)).check(matches(isChecked())) + goBack() + + // set expiration date + userShare.expirationDate = 1582019340000 + openAdvancedPermissions(sut, userShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) + goBack() + + userShare.expirationDate = 0 + openAdvancedPermissions(sut, userShare) + onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) + onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) + } + } } - - activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) } - shortSleep() - waitForIdleSync() - - // check if items are visible - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed()))) - - // click event - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) - - // validate view shown on screen - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(not(isDisplayed()))) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isDisplayed())) - - // read-only - userShare.permissions = 17 // from server - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked())) - goBack() - - // allow upload & editing - userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER // from server - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked())) - goBack() - - // file drop - userShare.permissions = 4 - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isChecked())) - goBack() - - // allow reshare - userShare.permissions = 1 // from server - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isNotChecked())) - goBack() - - userShare.permissions = 17 // from server - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isChecked())) - goBack() - - // set expiration date - userShare.expirationDate = 1582019340000 - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText("")))) - goBack() - - userShare.expirationDate = 0 - openAdvancedPermissions(sut, userShare) - onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked())) - onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText(""))) } // open bottom sheet with actions - private fun openAdvancedPermissions( - sut: FileDetailSharingFragment, - userShare: OCShare - ) { - activity.handler.post { - sut.showSharingMenuActionSheet(userShare) + private fun openAdvancedPermissions(sut: FileDetailSharingFragment, userShare: OCShare) { + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + activity.handler.post { + sut.showSharingMenuActionSheet(userShare) + } + EspressoIdlingResource.decrement() + onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) + } + } } - shortSleep() - waitForIdleSync() - onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) } // remove the fragment shown private fun goBack() { - activity.handler.post { - val processFragment = - activity.supportFragmentManager.findFragmentByTag(FileDetailsSharingProcessFragment.TAG) as - FileDetailsSharingProcessFragment - processFragment.onBackPressed() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + activity.handler.post { + val processFragment = + activity.supportFragmentManager.findFragmentByTag(FileDetailsSharingProcessFragment.TAG) as + FileDetailsSharingProcessFragment + processFragment.onBackPressed() + } + } + } } - shortSleep() - waitForIdleSync() } // also applies for @@ -705,126 +838,105 @@ class FileDetailSharingFragmentIT : AbstractIT() { @Test @Suppress("MagicNumber") fun userOptionMenuFolderSendNewEmail() { - val sut = FileDetailSharingFragment.newInstance(file, user) - activity.addFragment(sut) - setupSecondaryFragment() - shortSleep() - sut.refreshCapabilitiesFromDB() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + val sut = FileDetailSharingFragment.newInstance(file, user) + activity.addFragment(sut) + onIdleSync { + EspressoIdlingResource.increment() + setupSecondaryFragment() + sut.refreshCapabilitiesFromDB() + EspressoIdlingResource.decrement() - val userShare = OCShare().apply { - isFolder = true - shareType = ShareType.USER - permissions = 17 + val userShare = OCShare().apply { + isFolder = true + shareType = ShareType.USER + permissions = 17 + } + + verifySendNewEmail(sut, userShare) + } + } } - - verifySendNewEmail(sut, userShare) } /** * verify send new email note text */ - private fun verifySendNewEmail( - sut: FileDetailSharingFragment, - userShare: OCShare - ) { - activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) } + private fun verifySendNewEmail(sut: FileDetailSharingFragment, userShare: OCShare) { + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) } + EspressoIdlingResource.decrement() - waitForIdleSync() - // click event - onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).perform(ViewActions.click()) + // click event + onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).perform(ViewActions.click()) - // validate view shown on screen - onView(ViewMatchers.withId(R.id.note_text)).check(matches(isDisplayed())) + // validate view shown on screen + onView(ViewMatchers.withId(R.id.note_text)).check(matches(isDisplayed())) + } + } + } } @Test fun testUploadAndEditingSharePermissions() { - val share = OCShare().apply { - permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER + val testCases = mapOf( + MAXIMUM_PERMISSIONS_FOR_FOLDER to true, + NO_PERMISSION to false, + READ_PERMISSION_FLAG to false, + CREATE_PERMISSION_FLAG to false, + DELETE_PERMISSION_FLAG to false, + SHARE_PERMISSION_FLAG to false + ) + + val share = OCShare() + for ((permission, expected) in testCases) { + share.permissions = permission + assertEquals("Failed for permission: $permission", expected, SharePermissionManager.canEdit(share)) } - assertTrue(SharingMenuHelper.isUploadAndEditingAllowed(share)) - - share.permissions = NO_PERMISSION - assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share)) - - share.permissions = READ_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share)) - - share.permissions = CREATE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share)) - - share.permissions = DELETE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share)) - - share.permissions = SHARE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share)) } @Test - @Suppress("MagicNumber") fun testReadOnlySharePermissions() { - val share = OCShare().apply { - permissions = 17 + val testCases = mapOf( + READ_PERMISSION_FLAG to true, + NO_PERMISSION to false, + CREATE_PERMISSION_FLAG to false, + DELETE_PERMISSION_FLAG to false, + SHARE_PERMISSION_FLAG to false, + MAXIMUM_PERMISSIONS_FOR_FOLDER to false, + MAXIMUM_PERMISSIONS_FOR_FILE to false + ) + + val share = OCShare() + for ((permission, expected) in testCases) { + share.permissions = permission + assertEquals("Failed for permission: $permission", expected, SharePermissionManager.isViewOnly(share)) } - assertTrue(SharingMenuHelper.isReadOnly(share)) - - share.permissions = NO_PERMISSION - assertFalse(SharingMenuHelper.isReadOnly(share)) - - share.permissions = READ_PERMISSION_FLAG - assertTrue(SharingMenuHelper.isReadOnly(share)) - - share.permissions = CREATE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isReadOnly(share)) - - share.permissions = DELETE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isReadOnly(share)) - - share.permissions = SHARE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isReadOnly(share)) - - share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER - assertFalse(SharingMenuHelper.isReadOnly(share)) - - share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE - assertFalse(SharingMenuHelper.isReadOnly(share)) } @Test - @Suppress("MagicNumber") - fun testFileDropSharePermissions() { + fun testFileRequestSharePermission() { + val testCases = mapOf( + CREATE_PERMISSION_FLAG to true, + NO_PERMISSION to false, + READ_PERMISSION_FLAG to false, + DELETE_PERMISSION_FLAG to false, + SHARE_PERMISSION_FLAG to false, + MAXIMUM_PERMISSIONS_FOR_FOLDER to false, + MAXIMUM_PERMISSIONS_FOR_FILE to false + ) + val share = OCShare().apply { - permissions = 4 + isFolder = true } - assertTrue(SharingMenuHelper.isFileDrop(share)) - share.permissions = NO_PERMISSION - assertFalse(SharingMenuHelper.isFileDrop(share)) - - share.permissions = READ_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isFileDrop(share)) - - share.permissions = CREATE_PERMISSION_FLAG - assertTrue(SharingMenuHelper.isFileDrop(share)) - - share.permissions = DELETE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isFileDrop(share)) - - share.permissions = SHARE_PERMISSION_FLAG - assertFalse(SharingMenuHelper.isFileDrop(share)) - - share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER - assertFalse(SharingMenuHelper.isFileDrop(share)) - - share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE - assertFalse(SharingMenuHelper.isFileDrop(share)) - } - - @After - override fun after() { - activity.storageManager.cleanShares() - activity.finish() - - super.after() + for ((permission, expected) in testCases) { + share.permissions = permission + assertEquals("Failed for permission: $permission", expected, SharePermissionManager.isFileRequest(share)) + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/GalleryFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/GalleryFragmentIT.kt index 4992af8..9712fe4 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/GalleryFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/GalleryFragmentIT.kt @@ -1,9 +1,10 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment @@ -11,69 +12,89 @@ import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.ThumbnailsCacheManager -import com.owncloud.android.datamodel.ThumbnailsCacheManager.InitDiskCacheTask import com.owncloud.android.datamodel.ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.files.model.ImageDimension +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest import org.junit.After import org.junit.Assert.assertNotNull import org.junit.Before -import org.junit.Rule import org.junit.Test import java.util.Random class GalleryFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) - - lateinit var activity: TestActivity - val random = Random(1) + private val testClassName = "com.owncloud.android.ui.fragment.GalleryFragmentIT" + private val random = Random(1) @Before - fun before() { - activity = testActivityRule.launchActivity(null) + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) // initialise thumbnails cache on background thread - InitDiskCacheTask().execute() + @Suppress("DEPRECATION") + ThumbnailsCacheManager.initDiskCacheAsync() } @After - override fun after() { + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) ThumbnailsCacheManager.clearCache() - - super.after() } + @Test + @UiThread @ScreenshotTest - @Test fun showEmpty() { - val sut = GalleryFragment() - activity.addFragment(sut) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = GalleryFragment() + activity.addFragment(sut) + EspressoIdlingResource.decrement() - waitForIdleSync() - - screenshot(activity) + val screenShotName = createName(testClassName + "_" + "showEmpty", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(activity, screenShotName) + } + } + } } @Test + @UiThread @ScreenshotTest fun showGallery() { - createImage(10000001, 700, 300) - createImage(10000002, 500, 300) - createImage(10000007, 300, 400) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + createImage(10000001, 700, 300) + createImage(10000002, 500, 300) + createImage(10000007, 300, 400) - val sut = GalleryFragment() - activity.addFragment(sut) + val sut = GalleryFragment() + activity.addFragment(sut) + EspressoIdlingResource.decrement() - waitForIdleSync() - shortSleep() - screenshot(activity) + val screenShotName = createName(testClassName + "_" + "showGallery", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(activity, screenShotName) + } + } + } } private fun createImage(id: Int, width: Int? = null, height: Int? = null) { diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/GroupfolderListFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/GroupfolderListFragmentIT.kt index 30177b1..ba41967 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/GroupfolderListFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/GroupfolderListFragmentIT.kt @@ -1,74 +1,98 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.android.lib.resources.groupfolders.Groupfolder import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest +import org.junit.After import org.junit.Before -import org.junit.Rule import org.junit.Test class GroupfolderListFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) - - lateinit var activity: TestActivity + private val testClassName = "com.owncloud.android.ui.fragment.GroupfolderListFragmentIT" @Before - fun before() { - activity = testActivityRule.launchActivity(null) + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) } @Test + @UiThread @ScreenshotTest fun showGroupfolder() { - val sut = GroupfolderListFragment() - activity.addFragment(sut) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() - shortSleep() // to let async task finish + val sut = GroupfolderListFragment() + activity.addFragment(sut) - activity.runOnUiThread { - sut.setAdapter(null) - sut.setData( - mapOf( - Pair("2", Groupfolder(2, "/subfolder/group")) - ) - ) + sut.setAdapter(null) + sut.setData( + mapOf( + Pair("2", Groupfolder(2, "/subfolder/group")) + ) + ) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showGroupfolder", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(activity, screenShotName) + } + } } - - waitForIdleSync() - shortSleep() - screenshot(activity) } @Test + @UiThread @ScreenshotTest fun showGroupfolders() { - val sut = GroupfolderListFragment() - activity.addFragment(sut) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() - shortSleep() // to let async task finish + val sut = GroupfolderListFragment() + activity.addFragment(sut) - activity.runOnUiThread { - sut.setAdapter(null) - sut.setData( - mapOf( - Pair("1", Groupfolder(1, "/test/")), - Pair("2", Groupfolder(2, "/subfolder/group")) - ) - ) + sut.setAdapter(null) + sut.setData( + mapOf( + Pair("1", Groupfolder(1, "/test/")), + Pair("2", Groupfolder(2, "/subfolder/group")) + ) + ) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showGroupfolders", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(activity, screenShotName) + } + } } - - waitForIdleSync() - shortSleep() - screenshot(activity) } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt index c11a315..793a6bc 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt @@ -1,364 +1,437 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment -import androidx.test.espresso.intent.rule.IntentsTestRule -import com.nextcloud.test.GrantStoragePermissionRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.test.GrantStoragePermissionRule.Companion.grant import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.lib.resources.shares.ShareeUser +import com.owncloud.android.lib.resources.tags.Tag +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.MimeType import com.owncloud.android.utils.ScreenshotTest +import org.junit.After import org.junit.Assert +import org.junit.Before import org.junit.Rule import org.junit.Test +import org.junit.rules.TestRule class OCFileListFragmentStaticServerIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + private val testClassName = "com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @get:Rule - val permissionRule = GrantStoragePermissionRule.grant() + var storagePermissionRule: TestRule = grant() @Test + @UiThread @ScreenshotTest @Suppress("MagicNumber") fun showFiles() { - val sut = testActivityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - OCFile("/1.png").apply { - mimeType = "image/png" - fileLength = 1024000 - modificationTimestamp = 1188206955000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - sut.storageManager.saveFile(this) + OCFile("/1.png").apply { + remoteId = "00000001" + mimeType = "image/png" + fileLength = 1024000 + modificationTimestamp = 1188206955000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + sut.storageManager.saveFile(this) + } + + OCFile("/image.png").apply { + remoteId = "00000002" + mimeType = "image/png" + isPreviewAvailable = false + fileLength = 3072000 + modificationTimestamp = 746443755000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + tags = listOf(Tag("", "Top secret", null)) + sut.storageManager.saveFile(this) + } + + OCFile("/live photo.png").apply { + remoteId = "00000003" + mimeType = "image/png" + isPreviewAvailable = false + fileLength = 3072000 + modificationTimestamp = 746443755000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + setLivePhoto("/video.mov") + sut.storageManager.saveFile(this) + } + + OCFile("/video.mp4").apply { + remoteId = "00000004" + mimeType = "video/mp4" + isPreviewAvailable = false + fileLength = 12092000 + modificationTimestamp = 746143952000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + tags = listOf(Tag("", "Confidential", null), Tag("", "+5", null)) + sut.storageManager.saveFile(this) + } + + sut.addFragment(OCFileListFragment()) + + val fragment = (sut.fragment as OCFileListFragment) + val root = sut.storageManager.getFileByEncryptedRemotePath("/") + fragment.listDirectory(root, false, false) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showFiles", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - - OCFile("/image.png").apply { - mimeType = "image/png" - isPreviewAvailable = false - fileLength = 3072000 - modificationTimestamp = 746443755000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - tags = listOf("Top secret") - sut.storageManager.saveFile(this) - } - - OCFile("/live photo.png").apply { - mimeType = "image/png" - isPreviewAvailable = false - fileLength = 3072000 - modificationTimestamp = 746443755000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - setLivePhoto("/video.mov") - sut.storageManager.saveFile(this) - } - - OCFile("/video.mp4").apply { - mimeType = "video/mp4" - isPreviewAvailable = false - fileLength = 12092000 - modificationTimestamp = 746143952000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - tags = listOf("Confidential", "+5") - sut.storageManager.saveFile(this) - } - - sut.addFragment(OCFileListFragment()) - - val fragment = (sut.fragment as OCFileListFragment) - val root = sut.storageManager.getFileByEncryptedRemotePath("/") - - shortSleep() - - sut.runOnUiThread { fragment.listDirectory(root, false, false) } - - waitForIdleSync() - - screenshot(sut) } /** * Use same values as {@link FileDetailSharingFragmentIT listSharesFileAllShareTypes } */ @Test + @UiThread @ScreenshotTest fun showSharedFiles() { - val sut = testActivityRule.launchActivity(null) - val fragment = OCFileListFragment() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - OCFile("/sharedToUser.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf(ShareeUser("Admin", "Server Admin", ShareType.USER)) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) + val fragment = OCFileListFragment() + + OCFile("/sharedToUser.jpg").apply { + remoteId = "00000001" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = listOf(ShareeUser("Admin", "Server Admin", ShareType.USER)) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/sharedToGroup.jpg").apply { + remoteId = "00000002" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = listOf(ShareeUser("group", "Group", ShareType.GROUP)) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/sharedToEmail.jpg").apply { + remoteId = "00000003" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = + listOf( + ShareeUser("admin@nextcloud.localhost", "admin@nextcloud.localhost", ShareType.EMAIL) + ) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/publicLink.jpg").apply { + remoteId = "00000004" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedViaLink = true + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/sharedToFederatedUser.jpg").apply { + remoteId = "00000005" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = listOf( + ShareeUser( + "admin@remote.nextcloud.com", + "admin@remote.nextcloud.com (remote)", + ShareType.FEDERATED + ) + ) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/sharedToPersonalCircle.jpg").apply { + remoteId = "00000006" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = listOf(ShareeUser("circle", "Circle (Personal circle)", ShareType.CIRCLE)) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/sharedToUserRoom.jpg").apply { + remoteId = "00000007" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = listOf(ShareeUser("Conversation", "Admin", ShareType.ROOM)) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/sharedToGroupRoom.jpg").apply { + remoteId = "00000008" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = listOf(ShareeUser("Conversation", "Meeting", ShareType.ROOM)) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/sharedToUsers.jpg").apply { + remoteId = "00000009" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + isSharedWithSharee = true + sharees = listOf( + ShareeUser("Admin", "Server Admin", ShareType.USER), + ShareeUser("User", "User", ShareType.USER), + ShareeUser("Christine", "Christine Scott", ShareType.USER) + ) + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/notShared.jpg").apply { + remoteId = "000000010" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + OCFile("/Foo%e2%80%aedm.exe").apply { + remoteId = "000000011" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + modificationTimestamp = 1000 + sut.storageManager.saveFile(this) + } + + sut.addFragment(fragment) + val root = sut.storageManager.getFileByEncryptedRemotePath("/") + fragment.listDirectory(root, false, false) + fragment.adapter.setShowShareAvatar(true) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showSharedFiles", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - - OCFile("/sharedToGroup.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf(ShareeUser("group", "Group", ShareType.GROUP)) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - OCFile("/sharedToEmail.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf(ShareeUser("admin@nextcloud.localhost", "admin@nextcloud.localhost", ShareType.EMAIL)) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - OCFile("/publicLink.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedViaLink = true - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - OCFile("/sharedToFederatedUser.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf( - ShareeUser("admin@remote.nextcloud.com", "admin@remote.nextcloud.com (remote)", ShareType.FEDERATED) - ) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - OCFile("/sharedToPersonalCircle.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf(ShareeUser("circle", "Circle (Personal circle)", ShareType.CIRCLE)) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - // as we cannot distinguish circle types, we do not need them right now -// OCFile("/sharedToPublicCircle.jpg").apply { -// parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId -// isSharedWithSharee = true -// sharees = listOf(ShareeUser("circle", "Circle (Public circle)", ShareType.CIRCLE)) -// modificationTimestamp = 1000 -// sut.storageManager.saveFile(this) -// } -// -// OCFile("/sharedToClosedCircle.jpg").apply { -// parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId -// isSharedWithSharee = true -// sharees = listOf(ShareeUser("circle", "Circle (Closed circle)", ShareType.CIRCLE)) -// modificationTimestamp = 1000 -// sut.storageManager.saveFile(this) -// } -// -// OCFile("/sharedToSecretCircle.jpg").apply { -// parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId -// isSharedWithSharee = true -// sharees = listOf(ShareeUser("circle", "Circle (Secret circle)", ShareType.CIRCLE)) -// modificationTimestamp = 1000 -// sut.storageManager.saveFile(this) -// } - - OCFile("/sharedToUserRoom.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf(ShareeUser("Conversation", "Admin", ShareType.ROOM)) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - OCFile("/sharedToGroupRoom.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf(ShareeUser("Conversation", "Meeting", ShareType.ROOM)) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - OCFile("/sharedToUsers.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - isSharedWithSharee = true - sharees = listOf( - ShareeUser("Admin", "Server Admin", ShareType.USER), - ShareeUser("User", "User", ShareType.USER), - ShareeUser("Christine", "Christine Scott", ShareType.USER) - ) - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - OCFile("/notShared.jpg").apply { - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - modificationTimestamp = 1000 - sut.storageManager.saveFile(this) - } - - sut.addFragment(fragment) - - shortSleep() - - val root = sut.storageManager.getFileByEncryptedRemotePath("/") - - sut.runOnUiThread { - fragment.listDirectory(root, false, false) - fragment.adapter.setShowShareAvatar(true) - } - - waitForIdleSync() - shortSleep() - shortSleep() - shortSleep() - - screenshot(sut) } /** * Use same values as {@link FileDetailSharingFragmentIT listSharesFileAllShareTypes } */ @Test + @UiThread @ScreenshotTest fun showFolderTypes() { - val sut = testActivityRule.launchActivity(null) - val fragment = OCFileListFragment() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - OCFile("/normal/").apply { - mimeType = MimeType.DIRECTORY - modificationTimestamp = 1624003571000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - sut.storageManager.saveFile(this) + val fragment = OCFileListFragment() + + OCFile("/normal/").apply { + remoteId = "00000001" + mimeType = MimeType.DIRECTORY + modificationTimestamp = 1624003571000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + sut.storageManager.saveFile(this) + } + + OCFile("/sharedViaLink/").apply { + remoteId = "00000002" + mimeType = MimeType.DIRECTORY + isSharedViaLink = true + modificationTimestamp = 1619003571000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + sut.storageManager.saveFile(this) + } + + OCFile("/share/").apply { + remoteId = "00000003" + mimeType = MimeType.DIRECTORY + isSharedWithSharee = true + modificationTimestamp = 1619303571000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + sut.storageManager.saveFile(this) + } + + OCFile("/groupFolder/").apply { + remoteId = "00000004" + mimeType = MimeType.DIRECTORY + modificationTimestamp = 1615003571000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + permissions += "M" + sut.storageManager.saveFile(this) + } + + OCFile("/encrypted/").apply { + remoteId = "00000005" + mimeType = MimeType.DIRECTORY + isEncrypted = true + decryptedRemotePath = "/encrypted/" + modificationTimestamp = 1614003571000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + sut.storageManager.saveFile(this) + } + + OCFile("/locked/").apply { + remoteId = "00000006" + mimeType = MimeType.DIRECTORY + isLocked = true + decryptedRemotePath = "/locked/" + modificationTimestamp = 1613003571000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + sut.storageManager.saveFile(this) + } + + OCFile("/offlineOperation/").apply { + mimeType = MimeType.DIRECTORY + decryptedRemotePath = "/offlineOperation/" + modificationTimestamp = System.currentTimeMillis() + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + sut.storageManager.saveFile(this) + } + + sut.addFragment(fragment) + + val root = sut.storageManager.getFileByEncryptedRemotePath("/") + fragment.listDirectory(root, false, false) + fragment.adapter.setShowShareAvatar(true) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showFolderTypes", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } } - - OCFile("/sharedViaLink/").apply { - mimeType = MimeType.DIRECTORY - isSharedViaLink = true - modificationTimestamp = 1619003571000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - sut.storageManager.saveFile(this) - } - - OCFile("/share/").apply { - mimeType = MimeType.DIRECTORY - isSharedWithSharee = true - modificationTimestamp = 1619303571000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - sut.storageManager.saveFile(this) - } - - OCFile("/groupFolder/").apply { - mimeType = MimeType.DIRECTORY - modificationTimestamp = 1615003571000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - permissions += "M" - sut.storageManager.saveFile(this) - } - - OCFile("/encrypted/").apply { - mimeType = MimeType.DIRECTORY - isEncrypted = true - decryptedRemotePath = "/encrypted/" - modificationTimestamp = 1614003571000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - sut.storageManager.saveFile(this) - } - - OCFile("/locked/").apply { - mimeType = MimeType.DIRECTORY - isLocked = true - decryptedRemotePath = "/locked/" - modificationTimestamp = 1613003571000 - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - sut.storageManager.saveFile(this) - } - - sut.addFragment(fragment) - - shortSleep() - - val root = sut.storageManager.getFileByEncryptedRemotePath("/") - - sut.runOnUiThread { - fragment.listDirectory(root, false, false) - fragment.adapter.setShowShareAvatar(true) - } - - waitForIdleSync() - shortSleep() - shortSleep() - shortSleep() - - screenshot(sut) } @Test + @UiThread @ScreenshotTest @Suppress("MagicNumber") fun showRichWorkspace() { - val sut = testActivityRule.launchActivity(null) - val fragment = OCFileListFragment() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - val folder = OCFile("/test/") - folder.setFolder() - sut.storageManager.saveFile(folder) + val fragment = OCFileListFragment() - val imageFile = OCFile("/test/image.png") - imageFile.mimeType = "image/png" - imageFile.fileLength = 1024000 - imageFile.modificationTimestamp = 1188206955000 - imageFile.parentId = sut.storageManager.getFileByEncryptedRemotePath("/test/").fileId - imageFile.storagePath = getFile("java.md").absolutePath - sut.storageManager.saveFile(imageFile) + val folder = OCFile("/test/") + folder.setFolder() + sut.storageManager.saveFile(folder) - sut.addFragment(fragment) - val testFolder: OCFile = sut.storageManager.getFileByEncryptedRemotePath("/test/") - testFolder.richWorkspace = getFile("java.md").readText() + val imageFile = OCFile("/test/image.png").apply { + remoteId = "00000001" + mimeType = "image/png" + fileLength = 1024000 + modificationTimestamp = 1188206955000 + parentId = sut.storageManager.getFileByEncryptedRemotePath("/test/").fileId + storagePath = getFile("java.md").absolutePath + } - sut.runOnUiThread { fragment.listDirectory(testFolder, false, false) } + sut.storageManager.saveFile(imageFile) - shortSleep() + sut.addFragment(fragment) + val testFolder: OCFile = sut.storageManager.getFileByEncryptedRemotePath("/test/") + testFolder.richWorkspace = getFile("java.md").readText() + fragment.listDirectory(testFolder, false, false) - screenshot(sut) + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showRichWorkspace", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } @Test + @UiThread fun shouldShowHeader() { - val activity = testActivityRule.launchActivity(null) - val sut = OCFileListFragment() + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = OCFileListFragment() + val folder = OCFile("/test/").apply { + remoteId = "000001" + setFolder() + } + activity.storageManager.saveFile(folder) + activity.addFragment(sut) + val testFolder: OCFile = activity.storageManager.getFileByEncryptedRemotePath("/test/") + EspressoIdlingResource.decrement() - val folder = OCFile("/test/") - folder.setFolder() - activity.storageManager.saveFile(folder) + // richWorkspace is not set + Assert.assertFalse(sut.adapter.shouldShowHeader()) - activity.addFragment(sut) - val testFolder: OCFile = activity.storageManager.getFileByEncryptedRemotePath("/test/") + EspressoIdlingResource.increment() + testFolder.richWorkspace = " " + activity.storageManager.saveFile(testFolder) + sut.adapter.swapDirectory(user, testFolder, activity.storageManager, false, "") + EspressoIdlingResource.decrement() - activity.runOnUiThread { - // richWorkspace is not set - Assert.assertFalse(sut.adapter.shouldShowHeader()) + Assert.assertFalse(sut.adapter.shouldShowHeader()) - testFolder.richWorkspace = " " - activity.storageManager.saveFile(testFolder) - sut.adapter.swapDirectory(user, testFolder, activity.storageManager, false, "") - Assert.assertFalse(sut.adapter.shouldShowHeader()) + EspressoIdlingResource.increment() + testFolder.richWorkspace = null + activity.storageManager.saveFile(testFolder) + sut.adapter.swapDirectory(user, testFolder, activity.storageManager, false, "") + EspressoIdlingResource.decrement() + Assert.assertFalse(sut.adapter.shouldShowHeader()) - testFolder.richWorkspace = null - activity.storageManager.saveFile(testFolder) - sut.adapter.swapDirectory(user, testFolder, activity.storageManager, false, "") - Assert.assertFalse(sut.adapter.shouldShowHeader()) + EspressoIdlingResource.increment() + testFolder.richWorkspace = "1" + activity.storageManager.saveFile(testFolder) + sut.adapter.setCurrentDirectory(testFolder) + EspressoIdlingResource.decrement() - testFolder.richWorkspace = "1" - activity.storageManager.saveFile(testFolder) - sut.adapter.setCurrentDirectory(testFolder) - Assert.assertTrue(sut.adapter.shouldShowHeader()) + Assert.assertTrue(sut.adapter.shouldShowHeader()) + } + } } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/SharedListFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/SharedListFragmentIT.kt index 48bea8b..9eabce7 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/SharedListFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/SharedListFragmentIT.kt @@ -3,168 +3,183 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment import android.view.View -import androidx.test.espresso.intent.rule.IntentsTestRule -import com.nextcloud.test.GrantStoragePermissionRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.nextcloud.test.GrantStoragePermissionRule.Companion.grant import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.resources.shares.OCShare import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test +import org.junit.rules.TestRule internal class SharedListFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) - - @get:Rule - val permissionRule = GrantStoragePermissionRule.grant() - - lateinit var sut: TestActivity + private val testClassName = "com.owncloud.android.ui.fragment.SharedListFragmentIT" @Before - fun before() { - sut = testActivityRule.launchActivity(null) + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) } + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @get:Rule + var storagePermissionRule: TestRule = grant() + @Test + @UiThread @ScreenshotTest fun showSharedFiles() { - val fragment = SharedListFragment() + launchActivity().use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + EspressoIdlingResource.increment() - val file = OCFile("/shared to admin.png").apply { - remoteId = "00000001" - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - mimeType = "image/png" - fileLength = 1024000 - modificationTimestamp = 1188206955 - permissions = OCFile.PERMISSION_CAN_RESHARE - sut.storageManager.saveFile(this) - } + val fragment = SharedListFragment() - val file1 = OCFile("/shared to group.png").apply { - remoteId = "00000001" - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - mimeType = "image/png" - fileLength = 1024000 - modificationTimestamp = 1188206955 - permissions = OCFile.PERMISSION_CAN_RESHARE - sut.storageManager.saveFile(this) - } + val file = OCFile("/shared to admin.png").apply { + remoteId = "00000001" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + mimeType = "image/png" + fileLength = 1024000 + modificationTimestamp = 1188206955 + permissions = OCFile.PERMISSION_CAN_RESHARE + sut.storageManager.saveFile(this) + } - val file2 = OCFile("/shared via public link.png").apply { - remoteId = "00000001" - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - mimeType = "image/png" - fileLength = 1024000 - modificationTimestamp = 1188206955 - permissions = OCFile.PERMISSION_CAN_RESHARE - sut.storageManager.saveFile(this) - } + val file1 = OCFile("/shared to group.png").apply { + remoteId = "00000001" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + mimeType = "image/png" + fileLength = 1024000 + modificationTimestamp = 1188206955 + permissions = OCFile.PERMISSION_CAN_RESHARE + sut.storageManager.saveFile(this) + } - val file3 = OCFile("/shared to personal circle.png").apply { - remoteId = "00000001" - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - mimeType = "image/png" - fileLength = 1024000 - modificationTimestamp = 1188206955 - permissions = OCFile.PERMISSION_CAN_RESHARE - sut.storageManager.saveFile(this) - } + val file2 = OCFile("/shared via public link.png").apply { + remoteId = "00000001" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + mimeType = "image/png" + fileLength = 1024000 + modificationTimestamp = 1188206955 + permissions = OCFile.PERMISSION_CAN_RESHARE + sut.storageManager.saveFile(this) + } - val file4 = OCFile("/shared to talk.png").apply { - remoteId = "00000001" - parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId - mimeType = "image/png" - fileLength = 1024000 - modificationTimestamp = 1188206955 - permissions = OCFile.PERMISSION_CAN_RESHARE - sut.storageManager.saveFile(this) - } + val file3 = OCFile("/shared to personal circle.png").apply { + remoteId = "00000001" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + mimeType = "image/png" + fileLength = 1024000 + modificationTimestamp = 1188206955 + permissions = OCFile.PERMISSION_CAN_RESHARE + sut.storageManager.saveFile(this) + } - val shares = listOf( - OCShare(file.decryptedRemotePath).apply { - remoteId = 1 - shareType = ShareType.USER - sharedWithDisplayName = "Admin" - permissions = OCShare.MAXIMUM_PERMISSIONS_FOR_FILE - userId = getUserId(user) - sharedDate = 1188206955 - mimetype = "image/png" - sut.storageManager.saveShare(this) - }, + val file4 = OCFile("/shared to talk.png").apply { + remoteId = "00000001" + parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId + mimeType = "image/png" + fileLength = 1024000 + modificationTimestamp = 1188206955 + permissions = OCFile.PERMISSION_CAN_RESHARE + sut.storageManager.saveFile(this) + } - OCShare(file1.decryptedRemotePath).apply { - remoteId = 2 - shareType = ShareType.GROUP - sharedWithDisplayName = "Group" - permissions = OCShare.MAXIMUM_PERMISSIONS_FOR_FILE - userId = getUserId(user) - sharedDate = 1188206955 - mimetype = "image/png" - sut.storageManager.saveShare(this) - }, + val shares = listOf( + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.USER + sharedWithDisplayName = "Admin" + permissions = OCShare.MAXIMUM_PERMISSIONS_FOR_FILE + userId = getUserId(user) + sharedDate = 1188206955 + mimetype = "image/png" + sut.storageManager.saveShare(this) + }, - OCShare(file2.decryptedRemotePath).apply { - remoteId = 3 - shareType = ShareType.PUBLIC_LINK - label = "Customer" - sharedDate = 1188206955 - mimetype = "image/png" - sut.storageManager.saveShare(this) - }, + OCShare(file1.decryptedRemotePath).apply { + remoteId = 2 + shareType = ShareType.GROUP + sharedWithDisplayName = "Group" + permissions = OCShare.MAXIMUM_PERMISSIONS_FOR_FILE + userId = getUserId(user) + sharedDate = 1188206955 + mimetype = "image/png" + sut.storageManager.saveShare(this) + }, - OCShare(file3.decryptedRemotePath).apply { - remoteId = 4 - shareType = ShareType.CIRCLE - sharedWithDisplayName = "Personal circle" - permissions = OCShare.SHARE_PERMISSION_FLAG - userId = getUserId(user) - sharedDate = 1188206955 - mimetype = "image/png" - sut.storageManager.saveShare(this) - }, + OCShare(file2.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.PUBLIC_LINK + label = "Customer" + sharedDate = 1188206955 + mimetype = "image/png" + sut.storageManager.saveShare(this) + }, - OCShare(file4.decryptedRemotePath).apply { - remoteId = 11 - shareType = ShareType.ROOM - sharedWithDisplayName = "Admin" - permissions = OCShare.SHARE_PERMISSION_FLAG - userId = getUserId(user) - sharedDate = 1188206955 - mimetype = "image/png" - sut.storageManager.saveShare(this) + OCShare(file3.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.CIRCLE + sharedWithDisplayName = "Personal circle" + permissions = OCShare.SHARE_PERMISSION_FLAG + userId = getUserId(user) + sharedDate = 1188206955 + mimetype = "image/png" + sut.storageManager.saveShare(this) + }, + + OCShare(file4.decryptedRemotePath).apply { + remoteId = 11 + shareType = ShareType.ROOM + sharedWithDisplayName = "Admin" + permissions = OCShare.SHARE_PERMISSION_FLAG + userId = getUserId(user) + sharedDate = 1188206955 + mimetype = "image/png" + sut.storageManager.saveShare(this) + } + ) + + sut.addFragment(fragment) + + fragment.isLoading = false + fragment.mEmptyListContainer?.visibility = View.GONE + fragment.adapter.setData( + shares, + SearchType.SHARED_FILTER, + storageManager, + null, + true + ) + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showSharedFiles", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } } - ) - - sut.addFragment(fragment) - - shortSleep() - - sut.runOnUiThread { - fragment.isLoading = false - fragment.mEmptyListContainer.visibility = View.GONE - fragment.adapter.setData( - shares, - SearchType.SHARED_FILTER, - storageManager, - null, - true - ) } - - waitForIdleSync() - shortSleep() - shortSleep() - shortSleep() - - screenshot(sut) } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFakeRepository.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFakeRepository.kt index d5b3d00..6173181 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFakeRepository.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFakeRepository.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt index dce1f59..8881262 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentIT.kt @@ -1,86 +1,109 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.fragment -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel -import org.junit.Rule +import com.owncloud.android.utils.EspressoIdlingResource +import org.junit.After +import org.junit.Before import org.junit.Test import java.io.File class UnifiedSearchFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) - @Test - fun showSearchResult() { - val activity = testActivityRule.launchActivity(null) - val sut = UnifiedSearchFragment.newInstance(null, null) + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } - activity.addFragment(sut) - - shortSleep() - - UiThreadStatement.runOnUiThread { - sut.onSearchResultChanged( - listOf( - UnifiedSearchSection( - providerID = "files", - name = "Files", - entries = listOf( - SearchResultEntry( - "thumbnailUrl", - "Test", - "in Files", - "http://localhost/nc/index.php/apps/files/?dir=/Files&scrollto=Test", - "icon", - false - ) - ), - hasMoreResults = false - ) - ) - ) - } - shortSleep() + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) } @Test + @UiThread + fun showSearchResult() { + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val sut = UnifiedSearchFragment.newInstance(null, null) + activity.addFragment(sut) + + sut.onSearchResultChanged( + listOf( + UnifiedSearchSection( + providerID = "files", + name = "Files", + entries = listOf( + SearchResultEntry( + "thumbnailUrl", + "Test", + "in Files", + "http://localhost/nc/index.php/apps/files/?dir=/Files&scrollto=Test", + "icon", + false + ) + ), + hasMoreResults = false + ) + ) + ) + EspressoIdlingResource.decrement() + onView(isRoot()).check(matches(isDisplayed())) + } + } + } + } + + @Test + @UiThread fun search() { - val activity = testActivityRule.launchActivity(null) as TestActivity - val sut = UnifiedSearchFragment.newInstance(null, null) - val testViewModel = UnifiedSearchViewModel(activity.application) - testViewModel.setConnectivityService(activity.connectivityServiceMock) - val localRepository = UnifiedSearchFakeRepository() - testViewModel.setRepository(localRepository) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() - val ocFile = OCFile("/folder/test1.txt").apply { - storagePath = "/sdcard/1.txt" - storageManager.saveFile(this) + val sut = UnifiedSearchFragment.newInstance(null, null) + val testViewModel = UnifiedSearchViewModel(activity.application) + testViewModel.setConnectivityService(activity.connectivityServiceMock) + val localRepository = UnifiedSearchFakeRepository() + testViewModel.setRepository(localRepository) + val ocFile = OCFile("/folder/test1.txt").apply { + storagePath = "/sdcard/1.txt" + storageManager.saveFile(this) + } + + File(ocFile.storagePath).createNewFile() + activity.addFragment(sut) + + sut.setViewModel(testViewModel) + sut.vm.setQuery("test") + sut.vm.initialQuery() + + EspressoIdlingResource.decrement() + onView(isRoot()).check(matches(isDisplayed())) + } + } } - - File(ocFile.storagePath).createNewFile() - - activity.addFragment(sut) - - shortSleep() - - UiThreadStatement.runOnUiThread { - sut.setViewModel(testViewModel) - sut.vm.setQuery("test") - sut.vm.initialQuery() - } - shortSleep() } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/helpers/FileOperationsHelperIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/helpers/FileOperationsHelperIT.kt index 40f96f1..bee5789 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/helpers/FileOperationsHelperIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/helpers/FileOperationsHelperIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.helpers diff --git a/app/src/androidTest/java/com/owncloud/android/ui/helpers/UriUploaderIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/helpers/UriUploaderIT.kt index 2ded4eb..bd776c1 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/helpers/UriUploaderIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/helpers/UriUploaderIT.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.helpers diff --git a/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewBitmapScreenshotIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewBitmapScreenshotIT.kt index 0587ddb..294c3aa 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewBitmapScreenshotIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewBitmapScreenshotIT.kt @@ -1,43 +1,63 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.preview import android.content.Intent -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.owncloud.android.AbstractIT +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class PreviewBitmapScreenshotIT : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.preview.PreviewBitmapScreenshotIT" companion object { private const val PNG_FILE_ASSET = "imageFile.png" } - @get:Rule - val testActivityRule = IntentsTestRule(PreviewBitmapActivity::class.java, true, false) + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun showBitmap() { val pngFile = getFile(PNG_FILE_ASSET) - - val activity = testActivityRule.launchActivity( - Intent().putExtra( - PreviewBitmapActivity.EXTRA_BITMAP_PATH, - pngFile.absolutePath - ) + val intent = Intent(targetContext, PreviewBitmapActivity::class.java).putExtra( + PreviewBitmapActivity.EXTRA_BITMAP_PATH, + pngFile.absolutePath ) - shortSleep() - waitForIdleSync() - - screenshot(activity) + launchActivity(intent).use { scenario -> + scenario.onActivity { sut -> + onIdleSync { + val screenShotName = createName(testClassName + "_" + "showBitmap", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewImageFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewImageFragmentIT.kt deleted file mode 100644 index d785f1c..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewImageFragmentIT.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.preview - -import androidx.test.espresso.intent.rule.IntentsTestRule -import com.nextcloud.test.TestActivity -import com.owncloud.android.AbstractIT -import org.junit.Rule - -class PreviewImageFragmentIT : AbstractIT() { - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) - - // Disabled for now due to strange failing when using entire test suite - // Findings so far: - // PreviewImageFragmentIT runs fine when only running this - // running it in whole test suite fails - // manually tried to execute LoadBitmapTask, but this does not start "doInBackground", but only creates class - - // @Test - // @ScreenshotTest - // fun corruptImage() { - // val activity = testActivityRule.launchActivity(null) - // - // val ocFile = OCFile("/test.png") - // val sut = PreviewImageFragment.newInstance(ocFile, true, false) - // - // activity.addFragment(sut) - // - // while (!sut.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { - // shortSleep() - // } - // - // screenshot(activity) - // } - // - // @Test - // @ScreenshotTest - // fun validImage() { - // val activity = testActivityRule.launchActivity(null) - // - // val ocFile = OCFile("/test.png") - // ocFile.storagePath = getFile("imageFile.png").absolutePath - // - // val sut = PreviewImageFragment.newInstance(ocFile, true, false) - // - // activity.addFragment(sut) - // - // while (!sut.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { - // shortSleep() - // } - // - // screenshot(activity) - // } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java b/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java deleted file mode 100644 index 93acf38..0000000 --- a/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -package com.owncloud.android.ui.preview; - -import com.owncloud.android.AbstractIT; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.utils.MimeTypeUtil; -import com.owncloud.android.utils.ScreenshotTest; - -import org.junit.Rule; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; - -import androidx.test.espresso.intent.rule.IntentsTestRule; - -public class PreviewTextFileFragmentTest extends AbstractIT { - @Rule public IntentsTestRule activityRule = new IntentsTestRule<>(FileDisplayActivity.class, - true, - false); - - @Test - @ScreenshotTest - public void displaySimpleTextFile() throws IOException { - FileDisplayActivity sut = activityRule.launchActivity(null); - - shortSleep(); - - File file = getDummyFile("nonEmpty.txt"); - OCFile test = new OCFile("/text.md"); - test.setMimeType(MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN); - test.setStoragePath(file.getAbsolutePath()); - sut.startTextPreview(test, false); - - shortSleep(); - - screenshot(sut); - } - - @Test - @ScreenshotTest - public void displayJavaSnippetFile() throws IOException { - FileDisplayActivity sut = activityRule.launchActivity(null); - - shortSleep(); - - File file = getFile("java.md"); - OCFile test = new OCFile("/java.md"); - test.setMimeType(MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN); - test.setStoragePath(file.getAbsolutePath()); - sut.startTextPreview(test, false); - - shortSleep(); - - screenshot(sut); - } -} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.kt b/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.kt new file mode 100644 index 0000000..d9063fd --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.kt @@ -0,0 +1,89 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.preview + +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import com.owncloud.android.AbstractIT +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.utils.EspressoIdlingResource +import com.owncloud.android.utils.MimeTypeUtil +import com.owncloud.android.utils.ScreenshotTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.io.IOException + +class PreviewTextFileFragmentTest : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.preview.PreviewTextFileFragmentTest" + + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } + + @Test + @ScreenshotTest + @UiThread + @Throws(IOException::class) + fun displaySimpleTextFile() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val test = OCFile("/text.md").apply { + mimeType = MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN + storagePath = getDummyFile("nonEmpty.txt").absolutePath + } + + onIdleSync { + EspressoIdlingResource.increment() + sut.startTextPreview(test, true) + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "displaySimpleTextFile", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } + + @Test + @ScreenshotTest + @UiThread + @Throws(IOException::class) + fun displayJavaSnippetFile() { + launchActivity().use { scenario -> + scenario.onActivity { sut -> + val test = OCFile("/java.md").apply { + mimeType = MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN + storagePath = getFile("java.md").absolutePath + } + + onIdleSync { + EspressoIdlingResource.increment() + sut.startTextPreview(test, true) + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "displayJavaSnippetFile", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(sut, screenShotName) + } + } + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/ui/preview/pdf/PreviewPdfFragmentScreenshotIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/preview/pdf/PreviewPdfFragmentScreenshotIT.kt index 052ba28..5c0d3c1 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/preview/pdf/PreviewPdfFragmentScreenshotIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/preview/pdf/PreviewPdfFragmentScreenshotIT.kt @@ -1,54 +1,70 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.preview.pdf -import androidx.lifecycle.Lifecycle -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.annotation.UiThread +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.utils.ScreenshotTest -import org.junit.Rule +import org.junit.After +import org.junit.Before import org.junit.Test class PreviewPdfFragmentScreenshotIT : AbstractIT() { + private val testClassName = "com.owncloud.android.ui.preview.pdf.PreviewPdfFragmentScreenshotIT" companion object { private const val PDF_FILE_ASSET = "test.pdf" } - @get:Rule - val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false) + @Before + fun registerIdlingResource() { + IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) + } + + @After + fun unregisterIdlingResource() { + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) + } @Test + @UiThread @ScreenshotTest fun showPdf() { - val activity = testActivityRule.launchActivity(null) + launchActivity().use { scenario -> + scenario.onActivity { activity -> + onIdleSync { + EspressoIdlingResource.increment() + val pdfFile = getFile(PDF_FILE_ASSET) + val ocFile = OCFile("/test.pdf").apply { + storagePath = pdfFile.absolutePath + } - val pdfFile = getFile(PDF_FILE_ASSET) - val ocFile = OCFile("/test.pdf").apply { - storagePath = pdfFile.absolutePath + val sut = PreviewPdfFragment.newInstance(ocFile) + activity.addFragment(sut) + sut.dismissSnack() + + EspressoIdlingResource.decrement() + + val screenShotName = createName(testClassName + "_" + "showPdf", "") + onView(isRoot()).check(matches(isDisplayed())) + screenshotViaName(activity, screenShotName) + } + } } - - val sut = PreviewPdfFragment.newInstance(ocFile) - activity.addFragment(sut) - - while (!sut.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { - shortSleep() - } - - activity.runOnUiThread { - sut.dismissSnack() - } - - shortSleep() - waitForIdleSync() - - screenshot(activity) } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt index 8f3abf5..d18188a 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinActivityIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.trashbin @@ -20,6 +20,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isRoot import com.owncloud.android.utils.EspressoIdlingResource import com.owncloud.android.AbstractIT import com.owncloud.android.MainApp +import com.owncloud.android.extensions.launchAndCapture import com.owncloud.android.lib.common.accounts.AccountUtils import com.owncloud.android.utils.ScreenshotTest import org.junit.After @@ -30,7 +31,9 @@ class TrashbinActivityIT : AbstractIT() { private val testClassName = "com.owncloud.android.ui.trashbin.TrashbinActivityIT" enum class TestCase { - ERROR, EMPTY, FILES + ERROR, + EMPTY, + FILES } @Before @@ -69,84 +72,50 @@ class TrashbinActivityIT : AbstractIT() { @UiThread @ScreenshotTest fun files() { - launchActivity().use { scenario -> - scenario.onActivity { sut -> - val trashbinRepository = TrashbinLocalRepository(TestCase.FILES) - sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) - onIdleSync { - EspressoIdlingResource.increment() - sut.loadFolder( - onComplete = { EspressoIdlingResource.decrement() }, - onError = { EspressoIdlingResource.decrement() } - ) - onView(isRoot()).check(matches(isDisplayed())) - val screenShotName = createName(testClassName + "_" + "files", "") - screenshotViaName(sut, screenShotName) - } - } - } + launchAndCapture(testClassName, "files", before = { sut -> + val trashbinRepository = TrashbinLocalRepository(TestCase.FILES) + sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) + sut.loadFolder( + onComplete = { EspressoIdlingResource.decrement() }, + onError = { EspressoIdlingResource.decrement() } + ) + }) } @Test @UiThread @ScreenshotTest fun empty() { - launchActivity().use { scenario -> - scenario.onActivity { sut -> - val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) - sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) - onIdleSync { - EspressoIdlingResource.increment() - sut.loadFolder( - onComplete = { EspressoIdlingResource.decrement() }, - onError = { EspressoIdlingResource.decrement() } - ) - onView(isRoot()).check(matches(isDisplayed())) - val screenShotName = createName(testClassName + "_" + "empty", "") - screenshotViaName(sut, screenShotName) - } - } - } + launchAndCapture(testClassName, "empty", before = { sut -> + val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) + sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) + sut.loadFolder( + onComplete = { EspressoIdlingResource.decrement() }, + onError = { EspressoIdlingResource.decrement() } + ) + }) } @Test @UiThread @ScreenshotTest fun loading() { - launchActivity().use { scenario -> - scenario.onActivity { sut -> - val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) - sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) - onIdleSync { - EspressoIdlingResource.increment() - sut.showInitialLoading() - EspressoIdlingResource.decrement() - val screenShotName = createName(testClassName + "_" + "loading", "") - onView(isRoot()).check(matches(isDisplayed())) - screenshotViaName(sut, screenShotName) - } - } - } + launchAndCapture(testClassName, "loading", before = { sut -> + val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) + sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) + sut.showInitialLoading() + }) } @Test @UiThread @ScreenshotTest fun normalUser() { - launchActivity().use { scenario -> - scenario.onActivity { sut -> - val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) - sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) - onIdleSync { - EspressoIdlingResource.increment() - sut.showUser() - EspressoIdlingResource.decrement() - val screenShotName = createName(testClassName + "_" + "normalUser", "") - onView(isRoot()).check(matches(isDisplayed())) - screenshotViaName(sut, screenShotName) - } - } - } + launchAndCapture(testClassName, "normalUser", before = { sut -> + val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) + sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) + sut.showUser() + }) } @Test @@ -165,19 +134,10 @@ class TrashbinActivityIT : AbstractIT() { putExtra(Intent.EXTRA_USER, "differentUser@https://nextcloud.localhost") } - launchActivity(intent).use { scenario -> - scenario.onActivity { sut -> - val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) - sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) - onIdleSync { - EspressoIdlingResource.increment() - sut.showUser() - EspressoIdlingResource.decrement() - val screenShotName = createName(testClassName + "_" + "differentUser", "") - onView(isRoot()).check(matches(isDisplayed())) - screenshotViaName(sut, screenShotName) - } - } - } + launchAndCapture(testClassName, "differentUser", intent = intent, before = { sut -> + val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY) + sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut) + sut.showUser() + }) } } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinLocalRepository.kt b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinLocalRepository.kt index 7042734..ebf5244 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinLocalRepository.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/trashbin/TrashbinLocalRepository.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.trashbin @@ -36,8 +36,10 @@ class TrashbinLocalRepository(private val testCase: TrashbinActivityIT.TestCase) "image/png", "/trashbin/test.png", "subFolder/test.png", - 1395847838, // random date - 1395847908 // random date + // random date + 1395847838, + // random date + 1395847908 ) ) files.add( @@ -46,8 +48,10 @@ class TrashbinLocalRepository(private val testCase: TrashbinActivityIT.TestCase) "image/jpeg", "/trashbin/image.jpg", "image.jpg", - 1395841858, // random date - 1395837858 // random date + // random date + 1395841858, + // random date + 1395837858 ) ) files.add( @@ -56,8 +60,10 @@ class TrashbinLocalRepository(private val testCase: TrashbinActivityIT.TestCase) "DIR", "/trashbin/folder/", "folder", - 1395347858, // random date - 1395849858 // random date + // random date + 1395347858, + // random date + 1395849858 ) ) diff --git a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java index 8c81d28..15d6e3f 100644 --- a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java +++ b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2017 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.util; @@ -25,6 +25,7 @@ import com.owncloud.android.datamodel.e2e.v1.decrypted.Encrypted; import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.e2ee.CsrHelper; +import com.owncloud.android.utils.crypto.CryptoHelper; import com.owncloud.android.utils.EncryptionUtils; import org.junit.Assert; @@ -44,7 +45,6 @@ import java.security.PrivateKey; import java.security.SecureRandom; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -57,7 +57,6 @@ import javax.crypto.Cipher; import static com.owncloud.android.utils.EncryptionUtils.decodeStringToBase64Bytes; import static com.owncloud.android.utils.EncryptionUtils.decryptFile; import static com.owncloud.android.utils.EncryptionUtils.decryptFolderMetaData; -import static com.owncloud.android.utils.EncryptionUtils.decryptPrivateKey; import static com.owncloud.android.utils.EncryptionUtils.decryptStringAsymmetric; import static com.owncloud.android.utils.EncryptionUtils.decryptStringSymmetric; import static com.owncloud.android.utils.EncryptionUtils.deserializeJSON; @@ -79,6 +78,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertArrayEquals; public class EncryptionTestIT extends AbstractIT { @Rule public RetryTestRule retryTestRule = new RetryTestRule(); @@ -149,7 +149,7 @@ public class EncryptionTestIT extends AbstractIT { byte[] key2 = decodeStringToBase64Bytes(decryptedString); - assertTrue(Arrays.equals(key1, key2)); + assertArrayEquals(key1, key2); } @Test @@ -164,7 +164,7 @@ public class EncryptionTestIT extends AbstractIT { byte[] key2 = decodeStringToBase64Bytes(decryptedString); - assertTrue(Arrays.equals(key1, key2)); + assertArrayEquals(key1, key2); } @Test(expected = BadPaddingException.class) @@ -261,13 +261,8 @@ public class EncryptionTestIT extends AbstractIT { byte[] privateKeyBytes = privateKey.getEncoded(); String privateKeyString = encodeBytesToBase64String(privateKeyBytes); - String encryptedString; - if (new Random().nextBoolean()) { - encryptedString = EncryptionUtils.encryptPrivateKey(privateKeyString, keyPhrase); - } else { - encryptedString = EncryptionUtils.encryptPrivateKeyOld(privateKeyString, keyPhrase); - } - String decryptedString = decryptPrivateKey(encryptedString, keyPhrase); + String encryptedString = CryptoHelper.INSTANCE.encryptPrivateKey(privateKeyString, keyPhrase); + String decryptedString = CryptoHelper.INSTANCE.decryptPrivateKey(encryptedString, keyPhrase); assertEquals(privateKeyString, decryptedString); } @@ -502,7 +497,7 @@ public class EncryptionTestIT extends AbstractIT { // de-serialize EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = deserializeJSON(encryptedJson, - new TypeToken() { + new TypeToken<>() { }); // decrypt diff --git a/app/src/androidTest/java/com/owncloud/android/util/ErrorMessageAdapterIT.java b/app/src/androidTest/java/com/owncloud/android/util/ErrorMessageAdapterIT.java index f072001..1e6b4ee 100644 --- a/app/src/androidTest/java/com/owncloud/android/util/ErrorMessageAdapterIT.java +++ b/app/src/androidTest/java/com/owncloud/android/util/ErrorMessageAdapterIT.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021-2022 Chris Narkiewicz * SPDX-FileCopyrightText: 2019-2021 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.util; diff --git a/app/src/androidTest/java/com/owncloud/android/utils/BitmapUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/BitmapUtilsIT.kt index ac851a0..d9cbcf8 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/BitmapUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/BitmapUtilsIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils diff --git a/app/src/androidTest/java/com/owncloud/android/utils/DisplayUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/DisplayUtilsIT.kt index de318ad..df69c14 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/DisplayUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/DisplayUtilsIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils diff --git a/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt b/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt index f4542e7..d750163 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils @@ -12,18 +12,15 @@ import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import androidx.test.platform.app.InstrumentationRegistry import org.junit.After -import org.junit.Assert.fail import org.junit.Before import org.junit.Test class DrawableUtilTests { - private var sut: DrawableUtil? = null private var context: Context? = null @Before fun setUp() { - sut = DrawableUtil() context = InstrumentationRegistry.getInstrumentation().context } @@ -32,18 +29,13 @@ class DrawableUtilTests { val bitmap: Bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888) val drawable = BitmapDrawable(context?.resources, bitmap) - val layerDrawable = sut?.addDrawableAsOverlay(drawable, drawable) + val layerDrawable = DrawableUtil.addDrawableAsOverlay(drawable, drawable) - if (layerDrawable == null) { - fail("Layer drawable expected to be not null") - } - - assert(layerDrawable?.numberOfLayers == 2) + assert(layerDrawable.numberOfLayers == 2) } @After fun destroy() { - sut = null context = null } } diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionTestUtils.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionTestUtils.kt index 0b0bf78..34d6621 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionTestUtils.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionTestUtils.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils @@ -118,7 +118,7 @@ nDO4ew== ) val users = mutableListOf( - DecryptedUser(userId, cert) + DecryptedUser(userId, cert, null) ) // val filedrop = mutableMapOf( diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt index e5b2cf9..50c9815 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt index 4d72bbd..3ab05fd 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt @@ -3,13 +3,14 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils import com.google.gson.reflect.TypeToken import com.nextcloud.client.account.MockUser import com.nextcloud.common.User +import com.nextcloud.utils.extensions.findMetadataKeyByUserId import com.owncloud.android.EncryptionIT import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.e2e.v1.decrypted.Data @@ -221,7 +222,7 @@ class EncryptionUtilsV2IT : EncryptionIT() { val metadataKeyBase64 = EncryptionUtils.generateKeyString() val metadataKey = EncryptionUtils.decodeStringToBase64Bytes(metadataKeyBase64) - val user = DecryptedUser("t1", encryptionTestUtils.t1PublicKey) + val user = DecryptedUser("t1", encryptionTestUtils.t1PublicKey, null) val encryptedUser = encryptionUtilsV2.encryptUser(user, metadataKey) assertNotEquals(encryptedUser.encryptedMetadataKey, metadataKeyBase64) @@ -274,6 +275,11 @@ class EncryptionUtilsV2IT : EncryptionIT() { arbitraryDataProvider ) + // V1 doesn't have decryptedMetadataKey so that we can ignore it for comparison + for (user in decrypted.users) { + user.decryptedMetadataKey = null + } + assertEquals(metadataFile, decrypted) } @@ -290,7 +296,8 @@ class EncryptionUtilsV2IT : EncryptionIT() { mimeType = MimeType.JPEG }, EncryptionUtils.generateIV(), - EncryptionUtils.generateUid(), // random string, not real tag + // random string, not real tag + EncryptionUtils.generateUid(), EncryptionUtils.generateKey(), metadataFile, storageManager @@ -404,8 +411,8 @@ class EncryptionUtilsV2IT : EncryptionIT() { assertTrue(true) // if we reach this, test is successful } - private fun generateDecryptedFileV1(): com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile { - return com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile().apply { + private fun generateDecryptedFileV1(): com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile = + com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile().apply { encrypted = Data().apply { key = EncryptionUtils.generateKeyString() filename = "Random filename.jpg" @@ -415,7 +422,6 @@ class EncryptionUtilsV2IT : EncryptionIT() { initializationVector = EncryptionUtils.generateKeyString() authenticationTag = EncryptionUtils.generateKeyString() } - } @Test fun testMigrateDecryptedV1ToV2() { @@ -488,7 +494,7 @@ class EncryptionUtilsV2IT : EncryptionIT() { var metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert) - metadataFile = encryptionUtilsV2.addShareeToMetadata(metadataFile, enc2.accountName, enc2Cert) + metadataFile = encryptionUtilsV2.addShareeToMetadata(metadataFile, enc2.accountName, enc2Cert, null) val encryptedMetadataFile = encryptionUtilsV2.encryptFolderMetadataFile( metadataFile, @@ -540,7 +546,12 @@ class EncryptionUtilsV2IT : EncryptionIT() { val enc1 = MockUser("enc1", "Nextcloud") val enc2 = MockUser("enc2", "Nextcloud") var metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert) - metadataFile = encryptionUtilsV2.addShareeToMetadata(metadataFile, enc2.accountName, enc2Cert) + metadataFile = encryptionUtilsV2.addShareeToMetadata( + metadataFile, + enc2.accountName, + enc2Cert, + metadataFile.users.findMetadataKeyByUserId(enc2.accountName) + ) assertEquals(2, metadataFile.users.size) @@ -585,7 +596,7 @@ class EncryptionUtilsV2IT : EncryptionIT() { ) val users = mutableListOf( - DecryptedUser(user.accountName, cert) + DecryptedUser(user.accountName, cert, null) ) metadata.keyChecksums.add(encryptionUtilsV2.hashMetadataKey(metadata.metadataKey)) @@ -733,8 +744,6 @@ class EncryptionUtilsV2IT : EncryptionIT() { |Rei/RGBQ==","userId": "john"}],"version": "2"} """.trimMargin() - val base64Metadata = EncryptionUtils.encodeStringToBase64String(metadata) - val privateKey = EncryptionUtils.PEMtoPrivateKey(encryptionTestUtils.t1PrivateKey) val certificateT1 = EncryptionUtils.convertCertFromString(encryptionTestUtils.t1PublicKey) val certificateEnc2 = EncryptionUtils.convertCertFromString(enc2Cert) @@ -745,23 +754,18 @@ class EncryptionUtilsV2IT : EncryptionIT() { metadata ) - val base64Ans = encryptionUtilsV2.extractSignedString(signed) - - // verify val certs = listOf( certificateEnc2, certificateT1 ) - assertTrue(encryptionUtilsV2.verifySignedMessage(signed, certs)) - assertTrue(encryptionUtilsV2.verifySignedMessage(base64Ans, base64Metadata, certs)) + + assertTrue(encryptionUtilsV2.verifySignedData(signed, certs)) } @Throws(Throwable::class) @Test fun sign() { val sut = "randomstring123" - val json = "randomstring123" - val jsonBase64 = EncryptionUtils.encodeStringToBase64String(json) val privateKey = EncryptionUtils.PEMtoPrivateKey(encryptionTestUtils.t1PrivateKey) val certificate = EncryptionUtils.convertCertFromString(encryptionTestUtils.t1PublicKey) @@ -772,15 +776,12 @@ class EncryptionUtilsV2IT : EncryptionIT() { sut ) - val base64Ans = encryptionUtilsV2.extractSignedString(signed) - - // verify val certs = listOf( EncryptionUtils.convertCertFromString(enc2Cert), certificate ) - assertTrue(encryptionUtilsV2.verifySignedMessage(signed, certs)) - assertTrue(encryptionUtilsV2.verifySignedMessage(base64Ans, jsonBase64, certs)) + + assertTrue(encryptionUtilsV2.verifySignedData(signed, certs)) } @Test @@ -856,6 +857,11 @@ class EncryptionUtilsV2IT : EncryptionIT() { arbitraryDataProvider ) + // V1 doesn't have decryptedMetadataKey so that we can ignore it for comparison + for (user in decryptedFolderMetadata2.users) { + user.decryptedMetadataKey = null + } + // compare assertTrue( EncryptionTestIT.compareJsonStrings( diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EspressoIdlingResource.kt b/app/src/androidTest/java/com/owncloud/android/utils/EspressoIdlingResource.kt index e01c833..0b05ee9 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EspressoIdlingResource.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EspressoIdlingResource.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/app/src/androidTest/java/com/owncloud/android/utils/FileExportUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/FileExportUtilsIT.kt index d7c81b9..2aa84ed 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/FileExportUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/FileExportUtilsIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils diff --git a/app/src/androidTest/java/com/owncloud/android/utils/FileStorageUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/FileStorageUtilsIT.kt index 36453fe..04b5dba 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/FileStorageUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/FileStorageUtilsIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils @@ -141,7 +141,6 @@ class FileStorageUtilsIT : AbstractIT() { assertEquals("Internal storage/", pathToUserFriendlyDisplay("/storage/emulated/0/")) } - private fun pathToUserFriendlyDisplay(path: String): String { - return pathToUserFriendlyDisplay(path, targetContext, targetContext.resources) - } + private fun pathToUserFriendlyDisplay(path: String): String = + pathToUserFriendlyDisplay(path, targetContext, targetContext.resources) } diff --git a/app/src/androidTest/java/com/owncloud/android/utils/FileUtilTest.kt b/app/src/androidTest/java/com/owncloud/android/utils/FileUtilTest.kt index b44487f..6c27e85 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/FileUtilTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/FileUtilTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils diff --git a/app/src/androidTest/java/com/owncloud/android/utils/SessionMixinTest.kt b/app/src/androidTest/java/com/owncloud/android/utils/SessionMixinTest.kt index a0e33a8..11914ed 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/SessionMixinTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/SessionMixinTest.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.owncloud.android.utils diff --git a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt index 5e31285..310affd 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils @@ -11,6 +11,7 @@ import com.owncloud.android.AbstractIT import com.owncloud.android.datamodel.MediaFolder import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.utils.SyncedFolderUtils.hasExcludePrefix import org.apache.commons.io.FileUtils import org.junit.AfterClass import org.junit.Assert @@ -205,6 +206,21 @@ class SyncedFolderUtilsTest : AbstractIT() { Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder)) } + @Test + fun testInstantUploadPathIgnoreExcludedPrefixes() { + val testFiles = listOf( + "IMG_nnn.jpg", + "my_documents", + "Music", + ".trashed_IMG_nnn.jpg", + ".pending_IMG_nnn.jpg", + ".nomedia", + ".thumbdata_IMG_nnn", + ".thumbnail" + ).filter { !hasExcludePrefix(it) } + Assert.assertTrue(testFiles.size == 3) + } + companion object { private const val SELFIE = "selfie.png" private const val SCREENSHOT = "screenshot.JPG" diff --git a/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt index 98b410f..c27e104 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils.theme @@ -17,9 +17,12 @@ import org.junit.Test class CapabilityUtilsIT : AbstractIT() { @Test fun checkOutdatedWarning() { - assertFalse(test(NextcloudVersion.nextcloud_28)) - assertFalse(test(NextcloudVersion.nextcloud_27)) + assertFalse(test(NextcloudVersion.nextcloud_31)) + assertFalse(test(NextcloudVersion.nextcloud_30)) + assertTrue(test(NextcloudVersion.nextcloud_29)) + assertTrue(test(NextcloudVersion.nextcloud_28)) + assertTrue(test(NextcloudVersion.nextcloud_27)) assertTrue(test(NextcloudVersion.nextcloud_26)) assertTrue(test(NextcloudVersion.nextcloud_25)) assertTrue(test(NextcloudVersion.nextcloud_24)) @@ -27,13 +30,16 @@ class CapabilityUtilsIT : AbstractIT() { assertTrue(test(NextcloudVersion.nextcloud_22)) assertTrue(test(NextcloudVersion.nextcloud_21)) assertTrue(test(OwnCloudVersion.nextcloud_20)) - assertTrue(test(OwnCloudVersion.nextcloud_19)) - assertTrue(test(OwnCloudVersion.nextcloud_18)) - assertTrue(test(OwnCloudVersion.nextcloud_17)) - assertTrue(test(OwnCloudVersion.nextcloud_16)) } - private fun test(version: OwnCloudVersion): Boolean { - return CapabilityUtils.checkOutdatedWarning(targetContext.resources, version, false) + @Test + fun checkOutdatedWarningWithSubscription() { + assertFalse(test(NextcloudVersion.nextcloud_31)) + assertFalse(test(NextcloudVersion.nextcloud_30)) + + assertFalse(test(OwnCloudVersion.nextcloud_20, true)) } + + private fun test(version: OwnCloudVersion, hasValidSubscription: Boolean = false): Boolean = + CapabilityUtils.checkOutdatedWarning(targetContext.resources, version, false, hasValidSubscription) } diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml index 039d15e..4627fc2 100644 --- a/app/src/debug/AndroidManifest.xml +++ b/app/src/debug/AndroidManifest.xml @@ -3,7 +3,7 @@ ~ Nextcloud - Android Client ~ ~ SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> diff --git a/app/src/debug/java/com/nextcloud/client/di/BuildTypeComponentsModule.kt b/app/src/debug/java/com/nextcloud/client/di/BuildTypeComponentsModule.kt index 2dcce1e..b3022c7 100644 --- a/app/src/debug/java/com/nextcloud/client/di/BuildTypeComponentsModule.kt +++ b/app/src/debug/java/com/nextcloud/client/di/BuildTypeComponentsModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di diff --git a/app/src/debug/java/com/nextcloud/test/InjectionTestActivity.kt b/app/src/debug/java/com/nextcloud/test/InjectionTestActivity.kt index f9061f7..2e62f36 100644 --- a/app/src/debug/java/com/nextcloud/test/InjectionTestActivity.kt +++ b/app/src/debug/java/com/nextcloud/test/InjectionTestActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test @@ -17,7 +17,9 @@ import javax.inject.Inject /** * Sample activity to check test overriding injections */ -class InjectionTestActivity : AppCompatActivity(), Injectable { +class InjectionTestActivity : + AppCompatActivity(), + Injectable { @Inject lateinit var appPreferences: AppPreferences diff --git a/app/src/debug/java/com/nextcloud/test/TestActivity.kt b/app/src/debug/java/com/nextcloud/test/TestActivity.kt index 54a789f..e44aded 100644 --- a/app/src/debug/java/com/nextcloud/test/TestActivity.kt +++ b/app/src/debug/java/com/nextcloud/test/TestActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.test @@ -42,17 +42,13 @@ class TestActivity : private lateinit var binding: TestLayoutBinding val connectivityServiceMock: ConnectivityService = object : ConnectivityService { - override fun isConnected(): Boolean { - return false - } + override fun isNetworkAndServerAvailable(callback: ConnectivityService.GenericCallback) = Unit - override fun isInternetWalled(): Boolean { - return false - } + override fun isConnected(): Boolean = false - override fun getConnectivity(): Connectivity { - return Connectivity.CONNECTED_WIFI - } + override fun isInternetWalled(): Boolean = false + + override fun getConnectivity(): Connectivity = Connectivity.CONNECTED_WIFI } override fun onCreate(savedInstanceState: Bundle?) { @@ -96,9 +92,7 @@ class TestActivity : TODO("Not yet implemented") } - override fun getOperationsServiceBinder(): OperationsService.OperationsServiceBinder? { - return null - } + override fun getOperationsServiceBinder(): OperationsService.OperationsServiceBinder? = null override fun showSortListGroup(show: Boolean) { // not needed @@ -112,13 +106,9 @@ class TestActivity : TODO("Not yet implemented") } - override fun getFileUploaderHelper(): FileUploadHelper { - return FileUploadHelper.instance() - } + override fun getFileUploaderHelper(): FileUploadHelper = FileUploadHelper.instance() - override fun getFileDownloadProgressListener(): FileDownloadWorker.FileDownloadProgressListener? { - return null - } + override fun getFileDownloadProgressListener(): FileDownloadWorker.FileDownloadProgressListener? = null override fun getStorageManager(): FileDataStorageManager { if (!this::storage.isInitialized) { diff --git a/app/src/debug/res/layout/activity_injection_test.xml b/app/src/debug/res/layout/activity_injection_test.xml index 522b666..2f83d13 100644 --- a/app/src/debug/res/layout/activity_injection_test.xml +++ b/app/src/debug/res/layout/activity_injection_test.xml @@ -4,7 +4,7 @@ ~ ~ SPDX-FileCopyrightText: 2023 Álvaro Brey ~ SPDX-FileCopyrightText: 2023 Nextcloud GmbH - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> ~ SPDX-FileCopyrightText: 2020 Nextcloud GmbH - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.appReview @@ -11,8 +11,7 @@ import androidx.appcompat.app.AppCompatActivity import com.nextcloud.appReview.InAppReviewHelper import com.nextcloud.client.preferences.AppPreferences -class InAppReviewHelperImpl(appPreferences: AppPreferences) : - InAppReviewHelper { +class InAppReviewHelperImpl(appPreferences: AppPreferences) : InAppReviewHelper { override fun resetAndIncrementAppRestartCounter() { } diff --git a/app/src/generic/java/com/nextcloud/client/di/VariantComponentsModule.java b/app/src/generic/java/com/nextcloud/client/di/VariantComponentsModule.java index 1dd19f3..1b2a178 100644 --- a/app/src/generic/java/com/nextcloud/client/di/VariantComponentsModule.java +++ b/app/src/generic/java/com/nextcloud/client/di/VariantComponentsModule.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; diff --git a/app/src/generic/java/com/nextcloud/client/di/VariantModule.kt b/app/src/generic/java/com/nextcloud/client/di/VariantModule.kt index b9ded1e..d73f39e 100644 --- a/app/src/generic/java/com/nextcloud/client/di/VariantModule.kt +++ b/app/src/generic/java/com/nextcloud/client/di/VariantModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di @@ -16,7 +16,5 @@ import dagger.Reusable internal class VariantModule { @Provides @Reusable - fun scanOptionalFeature(): AppScanOptionalFeature { - return AppScanOptionalFeature.Stub - } + fun scanOptionalFeature(): AppScanOptionalFeature = AppScanOptionalFeature.Stub } diff --git a/app/src/generic/java/com/owncloud/android/utils/PushUtils.java b/app/src/generic/java/com/owncloud/android/utils/PushUtils.java index e6faf60..139377f 100644 --- a/app/src/generic/java/com/owncloud/android/utils/PushUtils.java +++ b/app/src/generic/java/com/owncloud/android/utils/PushUtils.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils; diff --git a/app/src/generic/java/com/owncloud/android/utils/SecurityUtils.java b/app/src/generic/java/com/owncloud/android/utils/SecurityUtils.java index 721f026..97b19a2 100644 --- a/app/src/generic/java/com/owncloud/android/utils/SecurityUtils.java +++ b/app/src/generic/java/com/owncloud/android/utils/SecurityUtils.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils; diff --git a/app/src/gplay/AndroidManifest.xml b/app/src/gplay/AndroidManifest.xml index 2de3aea..d566a24 100644 --- a/app/src/gplay/AndroidManifest.xml +++ b/app/src/gplay/AndroidManifest.xml @@ -4,7 +4,7 @@ ~ ~ SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors ~ SPDX-FileCopyrightText: 2017 Mario Danic - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> @@ -13,6 +13,8 @@ android:name="android.permission.REQUEST_INSTALL_PACKAGES" tools:node="remove"/> + + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.appReview @@ -85,11 +85,7 @@ class InAppReviewHelperImpl(val appPreferences: AppPreferences) : InAppReviewHel } } - private fun launchAppReviewFlow( - manager: ReviewManager, - activity: AppCompatActivity, - reviewInfo: ReviewInfo - ) { + private fun launchAppReviewFlow(manager: ReviewManager, activity: AppCompatActivity, reviewInfo: ReviewInfo) { val flow = manager.launchReviewFlow(activity, reviewInfo) flow.addOnCompleteListener { _ -> // The flow has finished. The API does not indicate whether the user diff --git a/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java b/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java index 849c3ca..6c807b6 100644 --- a/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java +++ b/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; diff --git a/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt b/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt index 8d7728a..627cb92 100644 --- a/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt +++ b/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di @@ -17,9 +17,7 @@ import dagger.Reusable internal class VariantModule { @Provides @Reusable - fun scanOptionalFeature(): AppScanOptionalFeature { - return object : AppScanOptionalFeature() { - override fun getScanContract() = ScanPageContract() - } + fun scanOptionalFeature(): AppScanOptionalFeature = object : AppScanOptionalFeature() { + override fun getScanContract() = ScanPageContract() } } diff --git a/app/src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java b/app/src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java index 8d5222d..81e03ec 100644 --- a/app/src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java +++ b/app/src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.authentication; diff --git a/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java b/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java index f2dddfe..ce6beea 100644 --- a/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java +++ b/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java @@ -3,12 +3,14 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.services.firebase; +import android.content.Intent; import android.text.TextUtils; +import com.google.firebase.messaging.Constants.MessageNotificationKeys; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import com.nextcloud.client.account.UserAccountManager; @@ -16,6 +18,7 @@ import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.jobs.NotificationWork; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.R; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.utils.PushUtils; import java.util.Map; @@ -30,14 +33,55 @@ public class NCFirebaseMessagingService extends FirebaseMessagingService { @Inject UserAccountManager accountManager; @Inject BackgroundJobManager backgroundJobManager; + static final String TAG = "NCFirebaseMessagingService"; + + // Firebase Messaging may apparently use two intent extras to specify a notification message. + // + // See the following fragments in https://github.com/firebase/firebase-android-sdk/blob/releases/m144_1.release/ + // firebase-messaging/src/main/java/com/google/firebase/messaging/FirebaseMessagingService.java#L223 + // firebase-messaging/src/main/java/com/google/firebase/messaging/NotificationParams.java#L419 + // firebase-messaging/src/main/java/com/google/firebase/messaging/Constants.java#L158 + // + // The "old" key is not exposed in com.google.firebase.messaging.Constants.MessageNotificationKeys, + // so we need to define it ourselves. + static final String ENABLE_NOTIFICATION_OLD = MessageNotificationKeys.NOTIFICATION_PREFIX_OLD + "e"; + static final String ENABLE_NOTIFICATION_NEW = MessageNotificationKeys.ENABLE_NOTIFICATION; + @Override public void onCreate() { super.onCreate(); AndroidInjection.inject(this); } + @Override + public void handleIntent(Intent intent) { + Log_OC.d(TAG, "handleIntent - extras: " + + ENABLE_NOTIFICATION_NEW + ": " + intent.getExtras().getString(ENABLE_NOTIFICATION_NEW) + ", " + + ENABLE_NOTIFICATION_OLD + ": " + intent.getExtras().getString(ENABLE_NOTIFICATION_OLD)); + + // When the app is in background and one of the ENABLE_NOTIFICATION or ENABLE_NOTIFICATION_OLD extras is set + // to "1" in the intent sent from the FCM system code to the FirebaseMessagingService in the application, + // the FCM library code that handles the intent DOES NOT invoke the onMessageReceived method. + // It just displays the notification by itself. + // + // In our case the original FCM message contains dummy values "NEW_NOTIFICATION" and we need to get the + // message in onMessageReceived to decrypt it. + // + // So we cheat here a little, by telling the FCM library that the notification flag is not set. + // + // Code below depends on implementation details of the firebase-messaging library (Firebase Android SDK). + // https://github.com/firebase/firebase-android-sdk/tree/master/firebase-messaging + + intent.removeExtra(ENABLE_NOTIFICATION_OLD); + intent.removeExtra(ENABLE_NOTIFICATION_NEW); + intent.putExtra(ENABLE_NOTIFICATION_NEW, "0"); + + super.handleIntent(intent); + } + @Override public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { + Log_OC.d(TAG, "onMessageReceived"); final Map data = remoteMessage.getData(); final String subject = data.get(NotificationWork.KEY_NOTIFICATION_SUBJECT); final String signature = data.get(NotificationWork.KEY_NOTIFICATION_SIGNATURE); @@ -48,6 +92,7 @@ public class NCFirebaseMessagingService extends FirebaseMessagingService { @Override public void onNewToken(@NonNull String newToken) { + Log_OC.d(TAG, "onNewToken"); super.onNewToken(newToken); if (!TextUtils.isEmpty(getResources().getString(R.string.push_server_url))) { diff --git a/app/src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.kt b/app/src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.kt index 77406b8..9a307dd 100644 --- a/app/src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.kt +++ b/app/src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils diff --git a/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java b/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java index 22df021..816612e 100644 --- a/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java +++ b/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2017-2018 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils; @@ -19,6 +19,7 @@ import com.google.gson.Gson; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -26,9 +27,7 @@ import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.PushConfigurationState; import com.owncloud.android.datamodel.SignatureVerification; import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; -import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForNotificationsOperation; @@ -131,14 +130,11 @@ public final class PushUtils { try { ocAccount = new OwnCloudAccount(account, context); - OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, context); + NextcloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getNextcloudClientFor(ocAccount, context); - RemoteOperation unregisterAccountDeviceForNotificationsOperation = new - UnregisterAccountDeviceForNotificationsOperation(); - - RemoteOperationResult remoteOperationResult = unregisterAccountDeviceForNotificationsOperation. - execute(mClient); + RemoteOperationResult remoteOperationResult = + new UnregisterAccountDeviceForNotificationsOperation().execute(mClient); if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) { String arbitraryValue; @@ -201,8 +197,8 @@ public final class PushUtils { TextUtils.isEmpty(providerValue)) { try { OwnCloudAccount ocAccount = new OwnCloudAccount(account, context); - OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, context); + NextcloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton(). + getNextcloudClientFor(ocAccount, context); RemoteOperationResult remoteOperationResult = new RegisterAccountDeviceForNotificationsOperation(pushTokenHash, diff --git a/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java b/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java index 954bba4..0ee14db 100644 --- a/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java +++ b/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils; diff --git a/app/src/gplay/res/values/setup.xml b/app/src/gplay/res/values/setup.xml index 1a44233..83fadc4 100644 --- a/app/src/gplay/res/values/setup.xml +++ b/app/src/gplay/res/values/setup.xml @@ -3,7 +3,7 @@ ~ Nextcloud - Android Client ~ ~ SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> diff --git a/app/src/huawei/AndroidManifest.xml b/app/src/huawei/AndroidManifest.xml index 27021d0..0178cc6 100644 --- a/app/src/huawei/AndroidManifest.xml +++ b/app/src/huawei/AndroidManifest.xml @@ -4,7 +4,7 @@ ~ ~ SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors ~ SPDX-FileCopyrightText: 2021 Tobias Kaminsky - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> diff --git a/app/src/huawei/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt b/app/src/huawei/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt index 067e774..3422424 100644 --- a/app/src/huawei/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt +++ b/app/src/huawei/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.appReview @@ -11,8 +11,7 @@ import androidx.appcompat.app.AppCompatActivity import com.nextcloud.appReview.InAppReviewHelper import com.nextcloud.client.preferences.AppPreferences -class InAppReviewHelperImpl(appPreferences: AppPreferences) : - InAppReviewHelper { +class InAppReviewHelperImpl(appPreferences: AppPreferences) : InAppReviewHelper { override fun resetAndIncrementAppRestartCounter() { } diff --git a/app/src/huawei/java/com/nextcloud/client/di/VariantComponentsModule.java b/app/src/huawei/java/com/nextcloud/client/di/VariantComponentsModule.java index 74822d8..ca426ec 100644 --- a/app/src/huawei/java/com/nextcloud/client/di/VariantComponentsModule.java +++ b/app/src/huawei/java/com/nextcloud/client/di/VariantComponentsModule.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di; diff --git a/app/src/huawei/java/com/nextcloud/client/di/VariantModule.kt b/app/src/huawei/java/com/nextcloud/client/di/VariantModule.kt index 8d7728a..627cb92 100644 --- a/app/src/huawei/java/com/nextcloud/client/di/VariantModule.kt +++ b/app/src/huawei/java/com/nextcloud/client/di/VariantModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.di @@ -17,9 +17,7 @@ import dagger.Reusable internal class VariantModule { @Provides @Reusable - fun scanOptionalFeature(): AppScanOptionalFeature { - return object : AppScanOptionalFeature() { - override fun getScanContract() = ScanPageContract() - } + fun scanOptionalFeature(): AppScanOptionalFeature = object : AppScanOptionalFeature() { + override fun getScanContract() = ScanPageContract() } } diff --git a/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt b/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt index 6fb04ae..a5272c0 100644 --- a/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt +++ b/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity diff --git a/app/src/huawei/java/com/owncloud/android/utils/PushUtils.java b/app/src/huawei/java/com/owncloud/android/utils/PushUtils.java index 3899810..bf1949a 100644 --- a/app/src/huawei/java/com/owncloud/android/utils/PushUtils.java +++ b/app/src/huawei/java/com/owncloud/android/utils/PushUtils.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils; diff --git a/app/src/huawei/java/com/owncloud/android/utils/SecurityUtils.java b/app/src/huawei/java/com/owncloud/android/utils/SecurityUtils.java index 721f026..97b19a2 100644 --- a/app/src/huawei/java/com/owncloud/android/utils/SecurityUtils.java +++ b/app/src/huawei/java/com/owncloud/android/utils/SecurityUtils.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.utils; diff --git a/app/src/huawei/res/values/bools.xml b/app/src/huawei/res/values/bools.xml index 107d34c..7e8bb54 100644 --- a/app/src/huawei/res/values/bools.xml +++ b/app/src/huawei/res/values/bools.xml @@ -4,7 +4,7 @@ ~ ~ SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors ~ SPDX-FileCopyrightText: 2021 Tobias Kaminsky - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> false diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8b1b9cc..7d0bf64 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,13 +2,13 @@ - @@ -47,12 +47,12 @@ + - @@ -126,6 +126,10 @@ android:usesCleartextTraffic="true" tools:ignore="UnusedAttribute" tools:replace="android:allowBackup"> + + + @@ -143,24 +147,93 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -188,10 +264,23 @@ + + + + @@ -235,6 +324,11 @@ android:name=".ui.activity.ContactsPreferenceActivity" android:exported="false" android:launchMode="singleInstance" /> + + + + + + + @@ -512,8 +616,7 @@ + android:label="@string/manage_space_title" /> - \ No newline at end of file + diff --git a/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt b/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt index 12b6cf2..5e3c7a6 100644 --- a/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt +++ b/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.files diff --git a/app/src/main/java/com/nextcloud/android/sso/Constants.java b/app/src/main/java/com/nextcloud/android/sso/Constants.java index a752b39..86dd2f8 100644 --- a/app/src/main/java/com/nextcloud/android/sso/Constants.java +++ b/app/src/main/java/com/nextcloud/android/sso/Constants.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2019 David Luhmer * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Edvard Holst - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.sso; diff --git a/app/src/main/java/com/nextcloud/android/sso/InputStreamBinder.java b/app/src/main/java/com/nextcloud/android/sso/InputStreamBinder.java index 6ef2e6e..31e061b 100644 --- a/app/src/main/java/com/nextcloud/android/sso/InputStreamBinder.java +++ b/app/src/main/java/com/nextcloud/android/sso/InputStreamBinder.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 David Luhmer - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only * * More information here: https://github.com/abeluck/android-streams-ipc */ @@ -237,7 +237,7 @@ public class InputStreamBinder extends IInputStreamService.Stub { case "POST": method = new PostMethod(requestUrl); if (requestBodyInputStream != null) { - RequestEntity requestEntity = new InputStreamRequestEntity(requestBodyInputStream, -1); + RequestEntity requestEntity = new InputStreamRequestEntity(requestBodyInputStream); ((PostMethod) method).setRequestEntity(requestEntity); } else if (request.getRequestBody() != null) { StringRequestEntity requestEntity = new StringRequestEntity( @@ -251,7 +251,7 @@ public class InputStreamBinder extends IInputStreamService.Stub { case "PATCH": method = new PatchMethod(requestUrl); if (requestBodyInputStream != null) { - RequestEntity requestEntity = new InputStreamRequestEntity(requestBodyInputStream, -1); + RequestEntity requestEntity = new InputStreamRequestEntity(requestBodyInputStream); ((PatchMethod) method).setRequestEntity(requestEntity); } else if (request.getRequestBody() != null) { StringRequestEntity requestEntity = new StringRequestEntity( @@ -265,7 +265,7 @@ public class InputStreamBinder extends IInputStreamService.Stub { case "PUT": method = new PutMethod(requestUrl); if (requestBodyInputStream != null) { - RequestEntity requestEntity = new InputStreamRequestEntity(requestBodyInputStream, -1); + RequestEntity requestEntity = new InputStreamRequestEntity(requestBodyInputStream); ((PutMethod) method).setRequestEntity(requestEntity); } else if (request.getRequestBody() != null) { StringRequestEntity requestEntity = new StringRequestEntity( @@ -502,12 +502,15 @@ public class InputStreamBinder extends IInputStreamService.Stub { @VisibleForTesting public static NameValuePair[] convertMapToNVP(Map map) { - NameValuePair[] nvp = new NameValuePair[map.size()]; + final var nvp = new NameValuePair[map.size()]; int i = 0; - for (String key : map.keySet()) { - nvp[i] = new NameValuePair(key, map.get(key)); + + for (Map.Entry entry : map.entrySet()) { + final var nameValuePair = new NameValuePair(entry.getKey(), entry.getValue()); + nvp[i] = nameValuePair; i++; } + return nvp; } diff --git a/app/src/main/java/com/nextcloud/android/sso/PatchMethod.java b/app/src/main/java/com/nextcloud/android/sso/PatchMethod.java index 7637471..34c5e8b 100644 --- a/app/src/main/java/com/nextcloud/android/sso/PatchMethod.java +++ b/app/src/main/java/com/nextcloud/android/sso/PatchMethod.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Timo Triebensky * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only * * More information here: https://github.com/abeluck/android-streams-ipc */ diff --git a/app/src/main/java/com/nextcloud/android/sso/PlainHeader.java b/app/src/main/java/com/nextcloud/android/sso/PlainHeader.java index b590706..07c23c6 100644 --- a/app/src/main/java/com/nextcloud/android/sso/PlainHeader.java +++ b/app/src/main/java/com/nextcloud/android/sso/PlainHeader.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.sso; diff --git a/app/src/main/java/com/nextcloud/android/sso/QueryParam.java b/app/src/main/java/com/nextcloud/android/sso/QueryParam.java index 25e42dd..21f7644 100644 --- a/app/src/main/java/com/nextcloud/android/sso/QueryParam.java +++ b/app/src/main/java/com/nextcloud/android/sso/QueryParam.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2021 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.sso; diff --git a/app/src/main/java/com/nextcloud/android/sso/Response.java b/app/src/main/java/com/nextcloud/android/sso/Response.java index 0bd992c..2402b8b 100644 --- a/app/src/main/java/com/nextcloud/android/sso/Response.java +++ b/app/src/main/java/com/nextcloud/android/sso/Response.java @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.sso; diff --git a/app/src/main/java/com/nextcloud/android/sso/aidl/IThreadListener.java b/app/src/main/java/com/nextcloud/android/sso/aidl/IThreadListener.java index 18e5d84..cf4ab01 100644 --- a/app/src/main/java/com/nextcloud/android/sso/aidl/IThreadListener.java +++ b/app/src/main/java/com/nextcloud/android/sso/aidl/IThreadListener.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017 David Luhmer - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.sso.aidl; diff --git a/app/src/main/java/com/nextcloud/android/sso/aidl/NextcloudRequest.java b/app/src/main/java/com/nextcloud/android/sso/aidl/NextcloudRequest.java index 4a3ebc7..2308e01 100644 --- a/app/src/main/java/com/nextcloud/android/sso/aidl/NextcloudRequest.java +++ b/app/src/main/java/com/nextcloud/android/sso/aidl/NextcloudRequest.java @@ -4,7 +4,7 @@ * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2021 Nextcloud GmbH * SPDX-FileCopyrightText: 2017 David Luhmer - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.sso.aidl; diff --git a/app/src/main/java/com/nextcloud/android/sso/aidl/ParcelFileDescriptorUtil.java b/app/src/main/java/com/nextcloud/android/sso/aidl/ParcelFileDescriptorUtil.java index ac51d60..fbbebdb 100644 --- a/app/src/main/java/com/nextcloud/android/sso/aidl/ParcelFileDescriptorUtil.java +++ b/app/src/main/java/com/nextcloud/android/sso/aidl/ParcelFileDescriptorUtil.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2017 David Luhmer - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.android.sso.aidl; diff --git a/app/src/main/java/com/nextcloud/appReview/AppReviewShownModel.kt b/app/src/main/java/com/nextcloud/appReview/AppReviewShownModel.kt index e906835..bd0acc0 100644 --- a/app/src/main/java/com/nextcloud/appReview/AppReviewShownModel.kt +++ b/app/src/main/java/com/nextcloud/appReview/AppReviewShownModel.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.appReview diff --git a/app/src/main/java/com/nextcloud/appReview/InAppReviewHelper.kt b/app/src/main/java/com/nextcloud/appReview/InAppReviewHelper.kt index 200efc6..34a054b 100644 --- a/app/src/main/java/com/nextcloud/appReview/InAppReviewHelper.kt +++ b/app/src/main/java/com/nextcloud/appReview/InAppReviewHelper.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.appReview diff --git a/app/src/main/java/com/nextcloud/appReview/InAppReviewModule.kt b/app/src/main/java/com/nextcloud/appReview/InAppReviewModule.kt index 331ea93..c2f4e92 100644 --- a/app/src/main/java/com/nextcloud/appReview/InAppReviewModule.kt +++ b/app/src/main/java/com/nextcloud/appReview/InAppReviewModule.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.appReview @@ -18,7 +18,6 @@ class InAppReviewModule { @Provides @Singleton - internal fun providesInAppReviewHelper(appPreferences: AppPreferences): InAppReviewHelper { - return InAppReviewHelperImpl(appPreferences) - } + internal fun providesInAppReviewHelper(appPreferences: AppPreferences): InAppReviewHelper = + InAppReviewHelperImpl(appPreferences) } diff --git a/app/src/main/java/com/nextcloud/client/NominatimClient.kt b/app/src/main/java/com/nextcloud/client/NominatimClient.kt index d5b07fd..7d8f35b 100644 --- a/app/src/main/java/com/nextcloud/client/NominatimClient.kt +++ b/app/src/main/java/com/nextcloud/client/NominatimClient.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023 ZetaTom * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client @@ -12,7 +12,7 @@ import com.google.gson.annotations.SerializedName import com.owncloud.android.MainApp import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.internal.http.HTTP_OK +import java.net.HttpURLConnection.HTTP_OK import java.net.URLEncoder class NominatimClient constructor(geocoderBaseUrl: String, email: String) { diff --git a/app/src/main/java/com/nextcloud/client/account/AnonymousUser.kt b/app/src/main/java/com/nextcloud/client/account/AnonymousUser.kt index 4df6a88..746e999 100644 --- a/app/src/main/java/com/nextcloud/client/account/AnonymousUser.kt +++ b/app/src/main/java/com/nextcloud/client/account/AnonymousUser.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account @@ -23,7 +23,9 @@ import java.net.URI * It serves as a semantically correct "empty value", allowing simplification of logic * in various components requiring user data, such as DB queries. */ -internal data class AnonymousUser(private val accountType: String) : User, Parcelable { +internal data class AnonymousUser(private val accountType: String) : + User, + Parcelable { companion object { @JvmStatic @@ -47,21 +49,14 @@ internal data class AnonymousUser(private val accountType: String) : User, Parce override val server = Server(URI.create(""), MainApp.MINIMUM_SUPPORTED_SERVER_VERSION) override val isAnonymous = true - override fun toPlatformAccount(): Account { - return Account(accountName, accountType) - } + override fun toPlatformAccount(): Account = Account(accountName, accountType) - override fun toOwnCloudAccount(): OwnCloudAccount { - return OwnCloudAccount(Uri.EMPTY, OwnCloudBasicCredentials("", "")) - } + override fun toOwnCloudAccount(): OwnCloudAccount = OwnCloudAccount(Uri.EMPTY, OwnCloudBasicCredentials("", "")) - override fun nameEquals(user: User?): Boolean { - return user?.accountName.equals(accountName, true) - } + override fun nameEquals(user: User?): Boolean = user?.accountName.equals(accountName, true) - override fun nameEquals(accountName: CharSequence?): Boolean { - return accountName?.toString().equals(this.accountType, true) - } + override fun nameEquals(accountName: CharSequence?): Boolean = + accountName?.toString().equals(this.accountType, true) override fun describeContents() = 0 diff --git a/app/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java b/app/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java index 0ff0744..b74bb3f 100644 --- a/app/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java +++ b/app/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java @@ -2,14 +2,13 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account; import android.accounts.Account; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; /** * This interface provides access to currently selected user. diff --git a/app/src/main/java/com/nextcloud/client/account/MockUser.kt b/app/src/main/java/com/nextcloud/client/account/MockUser.kt index 3b86547..7cbf717 100644 --- a/app/src/main/java/com/nextcloud/client/account/MockUser.kt +++ b/app/src/main/java/com/nextcloud/client/account/MockUser.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account @@ -20,7 +20,9 @@ import java.net.URI * This is a mock user object suitable for integration tests. Mocks obtained from code generators * such as Mockito or MockK cannot be transported in Intent extras. */ -data class MockUser(override val accountName: String, val accountType: String) : User, Parcelable { +data class MockUser(override val accountName: String, val accountType: String) : + User, + Parcelable { constructor() : this(DEFAULT_MOCK_ACCOUNT_NAME, DEFAULT_MOCK_ACCOUNT_TYPE) @@ -42,21 +44,14 @@ data class MockUser(override val accountName: String, val accountType: String) : override val server = Server(URI.create(""), MainApp.MINIMUM_SUPPORTED_SERVER_VERSION) override val isAnonymous = false - override fun toPlatformAccount(): Account { - return Account(accountName, accountType) - } + override fun toPlatformAccount(): Account = Account(accountName, accountType) - override fun toOwnCloudAccount(): OwnCloudAccount { - return OwnCloudAccount(Uri.EMPTY, OwnCloudBasicCredentials("", "")) - } + override fun toOwnCloudAccount(): OwnCloudAccount = OwnCloudAccount(Uri.EMPTY, OwnCloudBasicCredentials("", "")) - override fun nameEquals(user: User?): Boolean { - return user?.accountName.equals(accountName, true) - } + override fun nameEquals(user: User?): Boolean = user?.accountName.equals(accountName, true) - override fun nameEquals(accountName: CharSequence?): Boolean { - return accountName?.toString().equals(this.accountType, true) - } + override fun nameEquals(accountName: CharSequence?): Boolean = + accountName?.toString().equals(this.accountType, true) override fun describeContents() = 0 diff --git a/app/src/main/java/com/nextcloud/client/account/RegisteredUser.kt b/app/src/main/java/com/nextcloud/client/account/RegisteredUser.kt index 8d81d16..d73a99a 100644 --- a/app/src/main/java/com/nextcloud/client/account/RegisteredUser.kt +++ b/app/src/main/java/com/nextcloud/client/account/RegisteredUser.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account @@ -41,21 +41,14 @@ internal data class RegisteredUser( return account.name } - override fun toPlatformAccount(): Account { - return account - } + override fun toPlatformAccount(): Account = account - override fun toOwnCloudAccount(): OwnCloudAccount { - return ownCloudAccount - } + override fun toOwnCloudAccount(): OwnCloudAccount = ownCloudAccount - override fun nameEquals(user: User?): Boolean { - return nameEquals(user?.accountName) - } + override fun nameEquals(user: User?): Boolean = nameEquals(user?.accountName) - override fun nameEquals(accountName: CharSequence?): Boolean { - return accountName?.toString().equals(this.accountName, true) - } + override fun nameEquals(accountName: CharSequence?): Boolean = + accountName?.toString().equals(this.accountName, true) override fun describeContents() = 0 diff --git a/app/src/main/java/com/nextcloud/client/account/Server.kt b/app/src/main/java/com/nextcloud/client/account/Server.kt index 87367b8..63f4dfb 100644 --- a/app/src/main/java/com/nextcloud/client/account/Server.kt +++ b/app/src/main/java/com/nextcloud/client/account/Server.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account diff --git a/app/src/main/java/com/nextcloud/client/account/User.kt b/app/src/main/java/com/nextcloud/client/account/User.kt index 9651b72..856bb0b 100644 --- a/app/src/main/java/com/nextcloud/client/account/User.kt +++ b/app/src/main/java/com/nextcloud/client/account/User.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account @@ -11,7 +11,9 @@ import android.accounts.Account import android.os.Parcelable import com.owncloud.android.lib.common.OwnCloudAccount -interface User : Parcelable, com.nextcloud.common.User { +interface User : + Parcelable, + com.nextcloud.common.User { override val accountName: String val server: Server val isAnonymous: Boolean diff --git a/app/src/main/java/com/nextcloud/client/account/UserAccountManager.java b/app/src/main/java/com/nextcloud/client/account/UserAccountManager.java index 44156f6..a554987 100644 --- a/app/src/main/java/com/nextcloud/client/account/UserAccountManager.java +++ b/app/src/main/java/com/nextcloud/client/account/UserAccountManager.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account; diff --git a/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java b/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java index 7557c39..2bcc027 100644 --- a/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java +++ b/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 TSI-mc + * SPDX-FileCopyrightText: 2023-2024 TSI-mc * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.account; @@ -18,10 +18,11 @@ import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.text.TextUtils; -import android.util.Log; +import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.common.NextcloudClient; import com.nextcloud.utils.extensions.AccountExtensionsKt; +import com.nmc.android.ui.LauncherActivity; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; @@ -112,25 +113,78 @@ public class UserAccountManagerImpl implements UserAccountManager { @Override public boolean exists(Account account) { - Account[] nextcloudAccounts = getAccounts(); + try { + if (account == null) { + Log_OC.d(TAG, "account is null"); + return false; + } + + Account[] nextcloudAccounts = getAccounts(); + if (nextcloudAccounts.length == 0) { + Log_OC.d(TAG, "nextcloudAccounts are empty"); + return false; + } + + if (account.name.isEmpty()) { + Log_OC.d(TAG, "account name is empty"); + return false; + } - if (account != null && account.name != null) { int lastAtPos = account.name.lastIndexOf('@'); + if (lastAtPos == -1) { + Log_OC.d(TAG, "lastAtPos cannot be found"); + return false; + } + + boolean isLastAtPosInBoundsForHostAndPort = lastAtPos + 1 < account.name.length(); + if (!isLastAtPosInBoundsForHostAndPort) { + Log_OC.d(TAG, "lastAtPos not in bounds"); + return false; + } + String hostAndPort = account.name.substring(lastAtPos + 1); + String username = account.name.substring(0, lastAtPos); + if (hostAndPort.isEmpty() || username.isEmpty()) { + Log_OC.d(TAG, "hostAndPort or username is empty"); + return false; + } + String otherHostAndPort; String otherUsername; + for (Account otherAccount : nextcloudAccounts) { + // Skip null accounts or accounts with null names + if (otherAccount == null || otherAccount.name.isEmpty()) { + continue; + } + lastAtPos = otherAccount.name.lastIndexOf('@'); + + // Skip invalid account names + if (lastAtPos == -1) { + continue; + } + + boolean isLastAtPosInBoundsForOtherHostAndPort = lastAtPos + 1 < otherAccount.name.length(); + if (!isLastAtPosInBoundsForOtherHostAndPort) { + continue; + } otherHostAndPort = otherAccount.name.substring(lastAtPos + 1); + otherUsername = otherAccount.name.substring(0, lastAtPos); + if (otherHostAndPort.equals(hostAndPort) && otherUsername.equalsIgnoreCase(username)) { return true; } } + + return false; + } catch (Exception e) { + Log_OC.d(TAG, "Exception caught at UserAccountManagerImpl.exists(): " + e); + return false; } - return false; } @Override @@ -180,19 +234,20 @@ public class UserAccountManagerImpl implements UserAccountManager { */ @Nullable private User createUserFromAccount(@NonNull Account account) { - if (AccountExtensionsKt.isAnonymous(account, context)) { + Context safeContext = context != null ? context : MainApp.getAppContext(); + if (safeContext == null) { + Log_OC.e(TAG, "Unable to obtain a valid context"); return null; } - if (context == null) { - Log_OC.d(TAG, "Context is null MainApp.getAppContext() used"); - context = MainApp.getAppContext(); + if (AccountExtensionsKt.isAnonymous(account, safeContext)) { + return null; } OwnCloudAccount ownCloudAccount; try { - ownCloudAccount = new OwnCloudAccount(account, context); - } catch (AccountUtils.AccountNotFoundException ex) { + ownCloudAccount = new OwnCloudAccount(account, safeContext); + } catch (Exception ex) { return null; } @@ -212,7 +267,7 @@ public class UserAccountManagerImpl implements UserAccountManager { */ String serverAddressStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL); if (serverAddressStr == null || serverAddressStr.isEmpty()) { - return AnonymousUser.fromContext(context); + return AnonymousUser.fromContext(safeContext); } URI serverUri = URI.create(serverAddressStr); // TODO: validate @@ -398,6 +453,10 @@ public class UserAccountManagerImpl implements UserAccountManager { @Override public void startAccountCreation(final Activity activity) { + + // skipping AuthenticatorActivity redirection when user is on Launcher or FirstRun Activity + if (activity instanceof LauncherActivity || activity instanceof FirstRunActivity) return; + Intent intent = new Intent(context, AuthenticatorActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); diff --git a/app/src/main/java/com/nextcloud/client/appinfo/AppInfo.kt b/app/src/main/java/com/nextcloud/client/appinfo/AppInfo.kt index 3abb1a7..acc55a6 100644 --- a/app/src/main/java/com/nextcloud/client/appinfo/AppInfo.kt +++ b/app/src/main/java/com/nextcloud/client/appinfo/AppInfo.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.appinfo diff --git a/app/src/main/java/com/nextcloud/client/appinfo/AppInfoImpl.kt b/app/src/main/java/com/nextcloud/client/appinfo/AppInfoImpl.kt index 1f36ae6..9868e0a 100644 --- a/app/src/main/java/com/nextcloud/client/appinfo/AppInfoImpl.kt +++ b/app/src/main/java/com/nextcloud/client/appinfo/AppInfoImpl.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.appinfo @@ -16,17 +16,11 @@ class AppInfoImpl : AppInfo { override val versionCode: Int = BuildConfig.VERSION_CODE override val isDebugBuild: Boolean = BuildConfig.DEBUG - override fun getAppVersion(context: Context): String { - return try { - val pInfo = context.packageManager.getPackageInfo(context.packageName, 0) - if (pInfo != null) { - pInfo.versionName - } else { - "n/a" - } - } catch (e: PackageManager.NameNotFoundException) { - Log_OC.e(this, "Trying to get packageName", e.cause) - "n/a" - } + override fun getAppVersion(context: Context): String = try { + val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) + packageInfo.versionName ?: "n/a" + } catch (e: PackageManager.NameNotFoundException) { + Log_OC.e(this, "Trying to get packageName", e.cause) + "n/a" } } diff --git a/app/src/main/java/com/nextcloud/client/appinfo/AppInfoModule.kt b/app/src/main/java/com/nextcloud/client/appinfo/AppInfoModule.kt index 4fd0ff0..5e32efb 100644 --- a/app/src/main/java/com/nextcloud/client/appinfo/AppInfoModule.kt +++ b/app/src/main/java/com/nextcloud/client/appinfo/AppInfoModule.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.appinfo @@ -12,7 +12,5 @@ import dagger.Provides @Module class AppInfoModule { @Provides - fun appInfo(): AppInfo { - return AppInfoImpl() - } + fun appInfo(): AppInfo = AppInfoImpl() } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt index ba27fa0..6d123e2 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt @@ -1,66 +1,57 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.nextcloud.client.assistant.model.ScreenOverlayState +import com.nextcloud.client.assistant.model.ScreenState import com.nextcloud.client.assistant.repository.AssistantRepositoryType import com.owncloud.android.R -import com.owncloud.android.lib.resources.assistant.model.Task -import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import java.lang.ref.WeakReference -class AssistantViewModel( - private val repository: AssistantRepositoryType, - private val context: WeakReference -) : ViewModel() { +class AssistantViewModel(private val repository: AssistantRepositoryType) : ViewModel() { - sealed class State { - data object Idle : State() - data object Loading : State() - data class Error(val messageId: Int) : State() - data class TaskCreated(val messageId: Int) : State() - data class TaskDeleted(val messageId: Int) : State() - } + private val _screenState = MutableStateFlow(null) + val screenState: StateFlow = _screenState - private val _state = MutableStateFlow(State.Loading) - val state: StateFlow = _state + private val _screenOverlayState = MutableStateFlow(null) + val screenOverlayState: StateFlow = _screenOverlayState - private val _selectedTaskType = MutableStateFlow(null) - val selectedTaskType: StateFlow = _selectedTaskType + private val _snackbarMessageId = MutableStateFlow(null) + val snackbarMessageId: StateFlow = _snackbarMessageId - private val _taskTypes = MutableStateFlow?>(null) - val taskTypes: StateFlow?> = _taskTypes + private val _selectedTaskType = MutableStateFlow(null) + val selectedTaskType: StateFlow = _selectedTaskType - private var _taskList: List? = null + private val _taskTypes = MutableStateFlow?>(null) + val taskTypes: StateFlow?> = _taskTypes + + private var taskList: List? = null private val _filteredTaskList = MutableStateFlow?>(null) val filteredTaskList: StateFlow?> = _filteredTaskList init { fetchTaskTypes() - fetchTaskList() } @Suppress("MagicNumber") - fun createTask( - input: String, - type: String - ) { + fun createTask(input: String, taskType: TaskTypeData) { viewModelScope.launch(Dispatchers.IO) { - val result = repository.createTask(input, type) + val result = repository.createTask(input, taskType) val messageId = if (result.isSuccess) { R.string.assistant_screen_task_create_success_message @@ -68,62 +59,73 @@ class AssistantViewModel( R.string.assistant_screen_task_create_fail_message } - _state.update { - State.TaskCreated(messageId) - } + updateSnackbarMessage(messageId) delay(2000L) fetchTaskList() } } - fun selectTaskType(task: TaskType) { + fun selectTaskType(task: TaskTypeData) { _selectedTaskType.update { - filterTaskList(task.id) task } + + fetchTaskList() } private fun fetchTaskTypes() { viewModelScope.launch(Dispatchers.IO) { - val allTaskType = context.get()?.getString(R.string.assistant_screen_all_task_type) - val excludedIds = listOf("OCA\\ContextChat\\TextProcessing\\ContextChatTaskType") - val result = arrayListOf(TaskType(null, allTaskType, null)) val taskTypesResult = repository.getTaskTypes() - if (taskTypesResult.isSuccess) { - val excludedTaskTypes = taskTypesResult.resultData.types.filter { item -> item.id !in excludedIds } - result.addAll(excludedTaskTypes) - _taskTypes.update { - result.toList() - } - - selectTaskType(result.first()) - } else { - _state.update { - State.Error(R.string.assistant_screen_task_types_error_state_message) - } + if (taskTypesResult == null) { + updateSnackbarMessage(R.string.assistant_screen_task_types_error_state_message) + return@launch } + + if (taskTypesResult.isEmpty()) { + updateSnackbarMessage(R.string.assistant_screen_task_list_empty_message) + return@launch + } + + _taskTypes.update { + taskTypesResult + } + + selectTaskType(taskTypesResult.first()) } } - fun fetchTaskList(appId: String = "assistant", onCompleted: () -> Unit = {}) { + fun fetchTaskList() { viewModelScope.launch(Dispatchers.IO) { - val result = repository.getTaskList(appId) - if (result.isSuccess) { - _taskList = result.resultData.tasks + _screenState.update { + ScreenState.Refreshing + } - filterTaskList(_selectedTaskType.value?.id) - - _state.update { - State.Idle + val taskType = _selectedTaskType.value?.id ?: return@launch + val result = repository.getTaskList(taskType) + if (result != null) { + taskList = result + _filteredTaskList.update { + taskList?.sortedByDescending { task -> + task.id + } } - - onCompleted() + updateSnackbarMessage(null) } else { - _state.update { - State.Error(R.string.assistant_screen_task_list_error_state_message) - } + updateSnackbarMessage(R.string.assistant_screen_task_list_error_state_message) + } + + updateScreenState() + } + } + + private fun updateScreenState() { + _screenState.update { + if (_filteredTaskList.value?.isEmpty() == true) { + ScreenState.EmptyContent + } else { + ScreenState.Content } } } @@ -138,9 +140,7 @@ class AssistantViewModel( R.string.assistant_screen_task_delete_fail_message } - _state.update { - State.TaskDeleted(messageId) - } + updateSnackbarMessage(messageId) if (result.isSuccess) { removeTaskFromList(id) @@ -148,27 +148,15 @@ class AssistantViewModel( } } - fun resetState() { - _state.update { - State.Idle + fun updateSnackbarMessage(value: Int?) { + _snackbarMessageId.update { + value } } - private fun filterTaskList(taskTypeId: String?) { - if (taskTypeId == null) { - _filteredTaskList.update { - _taskList - } - } else { - _filteredTaskList.update { - _taskList?.filter { it.type == taskTypeId } - } - } - - _filteredTaskList.update { - it?.sortedByDescending { task -> - task.id - } + fun updateScreenState(value: ScreenOverlayState?) { + _screenOverlayState.update { + value } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 4dd567c..e19c6de 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -1,14 +1,13 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant import android.app.Activity -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -25,180 +24,250 @@ import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.pulltorefresh.PullToRefreshState +import androidx.compose.material3.pulltorefresh.pullToRefresh import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.nextcloud.client.assistant.component.AddTaskAlertDialog import com.nextcloud.client.assistant.component.CenterText -import com.nextcloud.client.assistant.taskTypes.TaskTypesRow -import com.nextcloud.client.assistant.task.TaskView +import com.nextcloud.client.assistant.extensions.getInputTitle +import com.nextcloud.client.assistant.model.ScreenOverlayState +import com.nextcloud.client.assistant.model.ScreenState import com.nextcloud.client.assistant.repository.AssistantMockRepository +import com.nextcloud.client.assistant.task.TaskView +import com.nextcloud.client.assistant.taskTypes.TaskTypesRow import com.nextcloud.ui.composeActivity.ComposeActivity import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog +import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet import com.owncloud.android.R -import com.owncloud.android.lib.resources.assistant.model.Task -import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData +import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.utils.DisplayUtils import kotlinx.coroutines.delay -import java.lang.ref.WeakReference +import kotlinx.coroutines.launch @Suppress("LongMethod") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) { - val state by viewModel.state.collectAsState() +fun AssistantScreen(viewModel: AssistantViewModel, capability: OCCapability, activity: Activity) { + val messageId by viewModel.snackbarMessageId.collectAsState() + val screenOverlayState by viewModel.screenOverlayState.collectAsState() + val selectedTaskType by viewModel.selectedTaskType.collectAsState() val filteredTaskList by viewModel.filteredTaskList.collectAsState() + val screenState by viewModel.screenState.collectAsState() val taskTypes by viewModel.taskTypes.collectAsState() - var showAddTaskAlertDialog by remember { mutableStateOf(false) } - var showDeleteTaskAlertDialog by remember { mutableStateOf(false) } - var taskIdToDeleted: Long? by remember { - mutableStateOf(null) - } + val scope = rememberCoroutineScope() val pullRefreshState = rememberPullToRefreshState() @Suppress("MagicNumber") - if (pullRefreshState.isRefreshing) { - LaunchedEffect(true) { - delay(1500) - viewModel.fetchTaskList(onCompleted = { - pullRefreshState.endRefresh() - }) - } + Box( + modifier = Modifier.pullToRefresh( + screenState == ScreenState.Refreshing, + pullRefreshState, + onRefresh = { + scope.launch { + delay(1500) + viewModel.fetchTaskList() + } + } + ) + ) { + ShowScreenState(screenState, selectedTaskType, taskTypes, viewModel, filteredTaskList, capability) + + ShowLinearProgressIndicator(screenState, pullRefreshState) + + AddFloatingActionButton( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(16.dp), + selectedTaskType, + viewModel + ) } - Box(Modifier.nestedScroll(pullRefreshState.nestedScrollConnection)) { - if (state == AssistantViewModel.State.Loading || pullRefreshState.isRefreshing) { + showSnackBarMessage(messageId, activity, viewModel) + ShowOverlayState(screenOverlayState, activity, viewModel) +} + +@Composable +private fun ShowScreenState( + screenState: ScreenState?, + selectedTaskType: TaskTypeData?, + taskTypes: List?, + viewModel: AssistantViewModel, + filteredTaskList: List?, + capability: OCCapability +) { + when (screenState) { + ScreenState.Refreshing -> { CenterText(text = stringResource(id = R.string.assistant_screen_loading)) - } else { - if (filteredTaskList.isNullOrEmpty()) { - EmptyTaskList(selectedTaskType, taskTypes, viewModel) - } else { - AssistantContent( - filteredTaskList!!, - taskTypes, - selectedTaskType, - viewModel, - showDeleteTaskAlertDialog = { taskId -> - taskIdToDeleted = taskId - showDeleteTaskAlertDialog = true - } - ) - } } - if (pullRefreshState.isRefreshing) { - LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) - } else { - LinearProgressIndicator(progress = { pullRefreshState.progress }, modifier = Modifier.fillMaxWidth()) + ScreenState.EmptyContent -> { + EmptyTaskList(selectedTaskType, taskTypes, viewModel) } - if (selectedTaskType?.name != stringResource(id = R.string.assistant_screen_all_task_type)) { - FloatingActionButton( - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(16.dp), - onClick = { - showAddTaskAlertDialog = true - } - ) { - Icon(Icons.Filled.Add, "Add Task Icon") - } - } - } - - ScreenState(state, activity, viewModel) - - if (showDeleteTaskAlertDialog) { - taskIdToDeleted?.let { id -> - SimpleAlertDialog( - title = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title), - description = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_description), - dismiss = { showDeleteTaskAlertDialog = false }, - onComplete = { viewModel.deleteTask(id) } + ScreenState.Content -> { + AssistantContent( + filteredTaskList ?: listOf(), + taskTypes, + selectedTaskType, + viewModel, + capability ) } - } - if (showAddTaskAlertDialog) { - selectedTaskType?.let { taskType -> - AddTaskAlertDialog( - title = taskType.name, - description = taskType.description, - addTask = { input -> - taskType.id?.let { - viewModel.createTask(input = input, type = it) - } - }, - dismiss = { - showAddTaskAlertDialog = false - } - ) - } + null -> Unit + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun ShowLinearProgressIndicator(screenState: ScreenState?, pullToRefreshState: PullToRefreshState) { + if (screenState == ScreenState.Refreshing) { + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + } else { + LinearProgressIndicator( + progress = { pullToRefreshState.distanceFraction }, + modifier = Modifier.fillMaxWidth() + ) } } @Composable -private fun ScreenState( - state: AssistantViewModel.State, - activity: Activity, +private fun AddFloatingActionButton( + modifier: Modifier, + selectedTaskType: TaskTypeData?, viewModel: AssistantViewModel ) { - val messageId: Int? = when (state) { - is AssistantViewModel.State.Error -> { - state.messageId - } - - is AssistantViewModel.State.TaskCreated -> { - state.messageId - } - - is AssistantViewModel.State.TaskDeleted -> { - state.messageId - } - - else -> { - null + FloatingActionButton( + modifier = modifier, + onClick = { + selectedTaskType?.let { + val newState = ScreenOverlayState.AddTask(it, "") + viewModel.updateScreenState(newState) + } } + ) { + Icon(Icons.Filled.Add, "Add Task Icon") } +} +private fun showSnackBarMessage(messageId: Int?, activity: Activity, viewModel: AssistantViewModel) { messageId?.let { DisplayUtils.showSnackMessage( activity, - stringResource(id = messageId) + activity.getString(it) ) - viewModel.resetState() + viewModel.updateSnackbarMessage(null) + } +} + +@Suppress("LongMethod") +@Composable +private fun ShowOverlayState(state: ScreenOverlayState?, activity: Activity, viewModel: AssistantViewModel) { + when (state) { + is ScreenOverlayState.AddTask -> { + AddTaskAlertDialog( + title = state.taskType.name, + description = state.taskType.description, + defaultInput = state.input, + addTask = { input -> + state.taskType.let { taskType -> + viewModel.createTask(input = input, taskType = taskType) + } + }, + dismiss = { + viewModel.updateScreenState(null) + } + ) + } + + is ScreenOverlayState.DeleteTask -> { + SimpleAlertDialog( + title = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title), + description = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_description), + dismiss = { viewModel.updateScreenState(null) }, + onComplete = { viewModel.deleteTask(state.id) } + ) + } + + is ScreenOverlayState.TaskActions -> { + val actions = state.getActions(activity, onEditCompleted = { addTask -> + viewModel.updateScreenState(addTask) + }, onDeleteCompleted = { deleteTask -> + viewModel.updateScreenState(deleteTask) + }) + + MoreActionsBottomSheet( + title = state.task.getInputTitle(), + actions = actions, + dismiss = { viewModel.updateScreenState(null) } + ) + } + + else -> Unit } } -@OptIn(ExperimentalFoundationApi::class) @Composable private fun AssistantContent( taskList: List, - taskTypes: List?, - selectedTaskType: TaskType?, + taskTypes: List?, + selectedTaskType: TaskTypeData?, viewModel: AssistantViewModel, - showDeleteTaskAlertDialog: (Long) -> Unit + capability: OCCapability ) { - LazyColumn( + Column(modifier = Modifier.fillMaxSize()) { + taskTypes?.let { + TaskTypesRow(selectedTaskType, data = taskTypes) { task -> + viewModel.selectTaskType(task) + } + } + + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(12.dp) + ) { + items(taskList) { task -> + TaskView( + task, + capability, + showTaskActions = { + val newState = ScreenOverlayState.TaskActions(task) + viewModel.updateScreenState(newState) + } + ) + Spacer(modifier = Modifier.height(8.dp)) + } + } + } +} + +@Composable +private fun EmptyTaskList( + selectedTaskType: TaskTypeData?, + taskTypes: List?, + viewModel: AssistantViewModel +) { + Column( modifier = Modifier .fillMaxSize() .padding(16.dp) ) { - stickyHeader { + taskTypes?.let { TaskTypesRow(selectedTaskType, data = taskTypes) { task -> viewModel.selectTaskType(task) } @@ -206,39 +275,15 @@ private fun AssistantContent( Spacer(modifier = Modifier.height(8.dp)) } - items(taskList) { task -> - TaskView(task, showDeleteTaskAlertDialog = { showDeleteTaskAlertDialog(task.id) }) - Spacer(modifier = Modifier.height(8.dp)) - } - } -} - -@Composable -private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List?, viewModel: AssistantViewModel) { - val text = if (selectedTaskType?.name == stringResource(id = R.string.assistant_screen_all_task_type)) { - stringResource(id = R.string.assistant_screen_no_task_available_for_all_task_filter_text) - } else { - stringResource( - id = R.string.assistant_screen_no_task_available_text, - selectedTaskType?.name ?: "" + CenterText( + text = stringResource( + id = R.string.assistant_screen_create_a_new_task_from_bottom_right_text + ) ) } - - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - TaskTypesRow(selectedTaskType, data = taskTypes) { task -> - viewModel.selectTaskType(task) - } - - Spacer(modifier = Modifier.height(8.dp)) - - CenterText(text = text) - } } +@Suppress("MagicNumber") @Composable @Preview private fun AssistantScreenPreview() { @@ -246,16 +291,17 @@ private fun AssistantScreenPreview() { MaterialTheme( content = { AssistantScreen( - viewModel = AssistantViewModel( - repository = mockRepository, - context = WeakReference(LocalContext.current) - ), - activity = ComposeActivity() + viewModel = AssistantViewModel(repository = mockRepository), + activity = ComposeActivity(), + capability = OCCapability().apply { + versionMayor = 30 + } ) } ) } +@Suppress("MagicNumber") @Composable @Preview private fun AssistantEmptyScreenPreview() { @@ -263,11 +309,11 @@ private fun AssistantEmptyScreenPreview() { MaterialTheme( content = { AssistantScreen( - viewModel = AssistantViewModel( - repository = mockRepository, - context = WeakReference(LocalContext.current) - ), - activity = ComposeActivity() + viewModel = AssistantViewModel(repository = mockRepository), + activity = ComposeActivity(), + capability = OCCapability().apply { + versionMayor = 30 + } ) } ) diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index b97f425..e9ccbb4 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.component @@ -22,13 +22,19 @@ import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog import com.owncloud.android.R @Composable -fun AddTaskAlertDialog(title: String?, description: String?, addTask: (String) -> Unit, dismiss: () -> Unit) { +fun AddTaskAlertDialog( + title: String, + description: String?, + defaultInput: String = "", + addTask: (String) -> Unit, + dismiss: () -> Unit +) { var input by remember { - mutableStateOf("") + mutableStateOf(defaultInput) } SimpleAlertDialog( - title = title ?: "", + title = title, description = description ?: "", dismiss = { dismiss() }, onComplete = { diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt index 2f87f96..cf3cefb 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt @@ -1,9 +1,9 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.component @@ -13,8 +13,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.sp +import com.owncloud.android.R @Composable fun CenterText(text: String) { @@ -22,7 +24,8 @@ fun CenterText(text: String) { Text( text = text, fontSize = 18.sp, - textAlign = TextAlign.Center + textAlign = TextAlign.Center, + color = colorResource(R.color.text_color) ) } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt index f2ff257..cef6c95 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt @@ -3,40 +3,130 @@ * * SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud * contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: MIT */ package com.nextcloud.client.assistant.extensions +import android.content.Context +import com.nextcloud.utils.date.DateFormatPattern +import com.nextcloud.utils.date.DateFormatter import com.owncloud.android.R -import com.owncloud.android.lib.resources.assistant.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.status.NextcloudVersion +import com.owncloud.android.lib.resources.status.OCCapability +import java.util.concurrent.TimeUnit + +fun Task.getInputAndOutput(): String { + val inputText = input?.input ?: "" + val outputText = output?.output ?: "" + + return "$inputText\n\n$outputText" +} + +fun Task.getInput(): String? = input?.input @Suppress("MagicNumber") -fun Task.statusData(): Pair { - return when (status) { - 0L -> { - Pair(R.drawable.ic_unknown, R.string.assistant_screen_unknown_task_status_text) - } - 1L -> { - Pair(R.drawable.ic_clock, R.string.assistant_screen_scheduled_task_status_text) - } - 2L -> { - Pair(R.drawable.ic_modification_desc, R.string.assistant_screen_running_task_text) - } - 3L -> { - Pair(R.drawable.ic_info, R.string.assistant_screen_successful_task_text) - } - 4L -> { - Pair(R.drawable.image_fail, R.string.assistant_screen_failed_task_text) - } - else -> { - Pair(R.drawable.ic_unknown, R.string.assistant_screen_unknown_task_status_text) - } +fun Task.getInputTitle(): String { + val maxTitleLength = 20 + val title = getInput() ?: "" + + return if (title.length > maxTitleLength) { + title.take(maxTitleLength) + "..." + } else { + title } } -// TODO add -fun Task.completionDateRepresentation(): String { - return completionExpectedAt ?: "TODO IMPLEMENT IT" +fun Task.getStatusIcon(capability: OCCapability): Int = + if (capability.version.isNewerOrEqual(NextcloudVersion.nextcloud_30)) { + getStatusIconV2() + } else { + getStatusIconV1() + } + +private fun Task.getStatusIconV1(): Int = when (status) { + "0" -> { + R.drawable.ic_unknown + } + "1" -> { + R.drawable.ic_clock + } + "2" -> { + R.drawable.ic_modification_desc + } + "3" -> { + R.drawable.ic_check_circle_outline + } + "4" -> { + R.drawable.image_fail + } + else -> { + R.drawable.ic_unknown + } +} + +private fun Task.getStatusIconV2(): Int = when (status) { + "STATUS_UNKNOWN" -> { + R.drawable.ic_unknown + } + "STATUS_SCHEDULED" -> { + R.drawable.ic_clock + } + "STATUS_RUNNING" -> { + R.drawable.ic_modification_desc + } + "STATUS_SUCCESSFUL" -> { + R.drawable.ic_check_circle_outline + } + "STATUS_FAILED" -> { + R.drawable.image_fail + } + else -> { + R.drawable.ic_unknown + } +} + +@Suppress("MagicNumber") +fun Task.getModifiedAtRepresentation(context: Context): String? { + if (lastUpdated == null) { + return null + } + + val modifiedAt = lastUpdated!!.toLong() + val currentTime = System.currentTimeMillis() / 1000 + val timeDifference = (currentTime - modifiedAt).toInt() + val timeDifferenceInMinutes = (timeDifference / 60) + val timeDifferenceInHours = (timeDifference / 3600) + + return when { + timeDifference < 0 -> { + context.getString(R.string.common_now) + } + + timeDifference < TimeUnit.MINUTES.toSeconds(1) -> { + context.resources.getQuantityString(R.plurals.time_seconds_ago, timeDifference, timeDifference) + } + + timeDifference < TimeUnit.HOURS.toSeconds(1) -> { + context.resources.getQuantityString( + R.plurals.time_minutes_ago, + timeDifferenceInMinutes, + timeDifferenceInMinutes + ) + } + + timeDifference < TimeUnit.DAYS.toSeconds(1) -> { + context.resources.getQuantityString( + R.plurals.time_hours_ago, + timeDifferenceInHours, + timeDifferenceInHours + ) + } + + else -> { + DateFormatter.timestampToDateRepresentation(modifiedAt, DateFormatPattern.MonthWithDate) + } + } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/model/ScreenOverlayState.kt b/app/src/main/java/com/nextcloud/client/assistant/model/ScreenOverlayState.kt new file mode 100644 index 0000000..02ceb33 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/model/ScreenOverlayState.kt @@ -0,0 +1,79 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.assistant.model + +import android.app.Activity +import com.nextcloud.client.assistant.extensions.getInput +import com.nextcloud.client.assistant.extensions.getInputAndOutput +import com.nextcloud.utils.extensions.showShareIntent +import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData +import com.owncloud.android.utils.ClipboardUtil + +sealed class ScreenOverlayState { + data class DeleteTask(val id: Long) : ScreenOverlayState() + data class AddTask(val taskType: TaskTypeData, val input: String) : ScreenOverlayState() + data class TaskActions(val task: Task) : ScreenOverlayState() { + private fun getInputAndOutput(): String = task.getInputAndOutput() + private fun getInput(): String? = task.getInput() + + private fun getCopyToClipboardAction(activity: Activity): Triple Unit> = Triple( + R.drawable.ic_content_copy, + R.string.common_copy + ) { + ClipboardUtil.copyToClipboard(activity, getInputAndOutput(), showToast = false) + } + + private fun getShareAction(activity: Activity): Triple Unit> = Triple( + R.drawable.ic_share, + R.string.common_share + ) { + activity.showShareIntent(getInputAndOutput()) + } + + private fun getEditAction(activity: Activity, onComplete: (AddTask) -> Unit): Triple Unit> = + Triple( + R.drawable.ic_edit, + R.string.action_edit + ) { + val taskType = TaskTypeData( + task.type, + activity.getString(R.string.assistant_screen_add_task_alert_dialog_title), + null, + emptyMap(), + emptyMap() + ) + val newState = AddTask(taskType, getInput() ?: "") + onComplete(newState) + } + + private fun getDeleteAction(onComplete: (DeleteTask) -> Unit): Triple Unit> = Triple( + R.drawable.ic_delete, + R.string.assistant_screen_task_more_actions_bottom_sheet_delete_action + ) { + val newState = DeleteTask(task.id) + onComplete(newState) + } + + fun getActions( + activity: Activity, + onEditCompleted: (AddTask) -> Unit, + onDeleteCompleted: (DeleteTask) -> Unit + ): List Unit>> = listOf( + getShareAction(activity), + getCopyToClipboardAction(activity), + getEditAction(activity, onComplete = { + onEditCompleted(it) + }), + getDeleteAction(onComplete = { + onDeleteCompleted(it) + }) + ) + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/model/ScreenState.kt b/app/src/main/java/com/nextcloud/client/assistant/model/ScreenState.kt new file mode 100644 index 0000000..33e206c --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/model/ScreenState.kt @@ -0,0 +1,14 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.assistant.model + +enum class ScreenState { + Refreshing, + EmptyContent, + Content +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index 04efe98..b130937 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -1,128 +1,68 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.repository import com.nextcloud.utils.extensions.getRandomString import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.assistant.model.Task -import com.owncloud.android.lib.resources.assistant.model.TaskList -import com.owncloud.android.lib.resources.assistant.model.TaskType -import com.owncloud.android.lib.resources.assistant.model.TaskTypes +import com.owncloud.android.lib.resources.assistant.v2.model.Shape +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskInput +import com.owncloud.android.lib.resources.assistant.v2.model.TaskOutput +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData @Suppress("MagicNumber") class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : AssistantRepositoryType { - override fun getTaskTypes(): RemoteOperationResult { - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { - resultData = TaskTypes( - listOf( - TaskType("1", "FreePrompt", "You can create free prompt text"), - TaskType("2", "Generate Headline", "You can create generate headline text") + override fun getTaskTypes(): List = listOf( + TaskTypeData( + id = "core:text2text", + name = "Free text to text prompt", + description = "Runs an arbitrary prompt through a language model that returns a reply", + inputShape = mapOf( + "input" to Shape( + name = "Prompt", + description = "Describe a task that you want the assistant to do or ask a question", + type = "Text" + ) + ), + outputShape = mapOf( + "output" to Shape( + name = "Generated reply", + description = "The generated text from the assistant", + type = "Text" ) ) - } - } + ) + ) - override fun createTask(input: String, type: String): RemoteOperationResult { - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) - } + override fun createTask(input: String, taskType: TaskTypeData): RemoteOperationResult = + RemoteOperationResult(RemoteOperationResult.ResultCode.OK) - override fun getTaskList(appId: String): RemoteOperationResult { - val taskList = if (giveEmptyTasks) { - TaskList(listOf()) - } else { - TaskList( - listOf( - Task( - 1, - "FreePrompt", - null, - "12", - "", - "Give me some long text 1", - "Lorem ipsum".getRandomString(100), - "" - ), - Task( - 2, - "GenerateHeadline", - null, - "12", - "", - "Give me some text 2", - "Lorem".getRandomString(100), - "", - "" - ), - Task( - 3, - "FreePrompt", - null, - "12", - "", - "Give me some text 3", - "Lorem".getRandomString(300), - "", - "" - ), - Task( - 4, - "FreePrompt", - null, - "12", - "", - "Give me some text 4", - "Lorem".getRandomString(300), - "", - "" - ), - Task( - 5, - "FreePrompt", - null, - "12", - "", - "Give me some text 5", - "Lorem".getRandomString(300), - "", - "" - ), - Task( - 6, - "FreePrompt", - null, - "12", - "", - "Give me some text 6", - "Lorem".getRandomString(300), - "", - "" - ), - Task( - 7, - "FreePrompt", - null, - "12", - "", - "Give me some text 7", - "Lorem".getRandomString(300), - "", - "" - ) - ) + override fun getTaskList(taskType: String): List = if (giveEmptyTasks) { + listOf() + } else { + listOf( + Task( + 1, + "FreePrompt", + null, + "12", + "", + TaskInput("Give me some long text 1"), + TaskOutput("Lorem ipsum".getRandomString(100)), + 1707692337, + 1707692337, + 1707692337, + 1707692337, + 1707692337 ) - } - - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { - resultData = taskList - } + ) } - override fun deleteTask(id: Long): RemoteOperationResult { - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) - } + override fun deleteTask(id: Long): RemoteOperationResult = + RemoteOperationResult(RemoteOperationResult.ResultCode.OK) } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt index 4b3dca4..13830cd 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt @@ -1,39 +1,80 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.repository import com.nextcloud.common.NextcloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.assistant.CreateTaskRemoteOperation -import com.owncloud.android.lib.resources.assistant.DeleteTaskRemoteOperation -import com.owncloud.android.lib.resources.assistant.GetTaskListRemoteOperation -import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation -import com.owncloud.android.lib.resources.assistant.model.TaskList -import com.owncloud.android.lib.resources.assistant.model.TaskTypes +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.resources.assistant.v1.CreateTaskRemoteOperationV1 +import com.owncloud.android.lib.resources.assistant.v1.DeleteTaskRemoteOperationV1 +import com.owncloud.android.lib.resources.assistant.v1.GetTaskListRemoteOperationV1 +import com.owncloud.android.lib.resources.assistant.v1.GetTaskTypesRemoteOperationV1 +import com.owncloud.android.lib.resources.assistant.v1.model.toV2 +import com.owncloud.android.lib.resources.assistant.v2.CreateTaskRemoteOperationV2 +import com.owncloud.android.lib.resources.assistant.v2.DeleteTaskRemoteOperationV2 +import com.owncloud.android.lib.resources.assistant.v2.GetTaskListRemoteOperationV2 +import com.owncloud.android.lib.resources.assistant.v2.GetTaskTypesRemoteOperationV2 +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData +import com.owncloud.android.lib.resources.status.NextcloudVersion +import com.owncloud.android.lib.resources.status.OCCapability -class AssistantRepository(private val client: NextcloudClient) : AssistantRepositoryType { +class AssistantRepository(private val client: NextcloudClient, capability: OCCapability) : AssistantRepositoryType { - override fun getTaskTypes(): RemoteOperationResult { - return GetTaskTypesRemoteOperation().execute(client) + private val supportsV2 = capability.version.isNewerOrEqual(NextcloudVersion.nextcloud_30) + + @Suppress("ReturnCount") + override fun getTaskTypes(): List? { + if (supportsV2) { + val result = GetTaskTypesRemoteOperationV2().execute(client) + if (result.isSuccess) { + return result.resultData + } + } else { + val result = GetTaskTypesRemoteOperationV1().execute(client) + if (result.isSuccess) { + return result.resultData.toV2() + } + } + + return null } - override fun createTask( - input: String, - type: String - ): RemoteOperationResult { - return CreateTaskRemoteOperation(input, type).execute(client) + override fun createTask(input: String, taskType: TaskTypeData): RemoteOperationResult = if (supportsV2) { + CreateTaskRemoteOperationV2(input, taskType).execute(client) + } else { + if (taskType.id.isNullOrEmpty()) { + RemoteOperationResult(ResultCode.CANCELLED) + } else { + CreateTaskRemoteOperationV1(input, taskType.id!!).execute(client) + } } - override fun getTaskList(appId: String): RemoteOperationResult { - return GetTaskListRemoteOperation(appId).execute(client) + @Suppress("ReturnCount") + override fun getTaskList(taskType: String): List? { + if (supportsV2) { + val result = GetTaskListRemoteOperationV2(taskType).execute(client) + if (result.isSuccess) { + return result.resultData.tasks.filter { it.appId == "assistant" } + } + } else { + val result = GetTaskListRemoteOperationV1("assistant").execute(client) + if (result.isSuccess) { + return result.resultData.toV2().tasks.filter { it.type == taskType } + } + } + + return null } - override fun deleteTask(id: Long): RemoteOperationResult { - return DeleteTaskRemoteOperation(id).execute(client) + override fun deleteTask(id: Long): RemoteOperationResult = if (supportsV2) { + DeleteTaskRemoteOperationV2(id).execute(client) + } else { + DeleteTaskRemoteOperationV1(id).execute(client) } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt index 8aaa9c3..048eee9 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt @@ -1,25 +1,21 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.repository import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.assistant.model.TaskList -import com.owncloud.android.lib.resources.assistant.model.TaskTypes +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData interface AssistantRepositoryType { - fun getTaskTypes(): RemoteOperationResult + fun getTaskTypes(): List? - fun createTask( - input: String, - type: String - ): RemoteOperationResult + fun createTask(input: String, taskType: TaskTypeData): RemoteOperationResult - fun getTaskList(appId: String): RemoteOperationResult + fun getTaskList(taskType: String): List? fun deleteTask(id: Long): RemoteOperationResult } diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt deleted file mode 100644 index c917988..0000000 --- a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.nextcloud.client.assistant.task - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.nextcloud.client.assistant.extensions.statusData -import com.owncloud.android.lib.resources.assistant.model.Task - -@Composable -fun TaskStatus(task: Task, foregroundColor: Color) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - val (iconId, descriptionId) = task.statusData() - - Image( - painter = painterResource(id = iconId), - modifier = Modifier.size(16.dp), - colorFilter = ColorFilter.tint(foregroundColor), - contentDescription = "status icon" - ) - - Spacer(modifier = Modifier.width(6.dp)) - - Text(text = stringResource(id = descriptionId), color = foregroundColor) - - /* - Spacer(modifier = Modifier.weight(1f)) - - Text(text = task.completionDateRepresentation(), color = foregroundColor) - - Spacer(modifier = Modifier.width(6.dp)) - */ - } -} diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatusView.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatusView.kt new file mode 100644 index 0000000..2f2f8d7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatusView.kt @@ -0,0 +1,151 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ + +package com.nextcloud.client.assistant.task + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.nextcloud.client.assistant.extensions.getModifiedAtRepresentation +import com.nextcloud.client.assistant.extensions.getStatusIcon +import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskInput +import com.owncloud.android.lib.resources.assistant.v2.model.TaskOutput +import com.owncloud.android.lib.resources.status.OCCapability +import java.util.concurrent.TimeUnit + +@Composable +fun TaskStatusView(task: Task, capability: OCCapability) { + val context = LocalContext.current + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + val iconId = task.getStatusIcon(capability) + val description = task.getModifiedAtRepresentation(context) + + Image( + painter = painterResource(id = iconId), + modifier = Modifier.size(16.dp), + colorFilter = ColorFilter.tint(color = colorResource(R.color.text_color)), + contentDescription = "status icon" + ) + + description?.let { + Spacer(modifier = Modifier.width(6.dp)) + Text(text = description, color = colorResource(R.color.text_color)) + } + } +} + +@Suppress("LongMethod", "MagicNumber") +@Composable +@Preview +private fun TaskStatusViewPreview() { + val currentTime = System.currentTimeMillis() / 1000 + + val tasks = listOf( + Task( + id = 1L, + type = "type1", + status = "STATUS_RUNNING", + userId = "user1", + appId = "app1", + input = TaskInput("input1"), + output = TaskOutput("output1"), + scheduledAt = currentTime.toInt(), + lastUpdated = currentTime.toInt() + ), + + Task( + id = 2L, + type = "type2", + status = "STATUS_SUCCESSFUL", + userId = "user2", + appId = "app2", + input = TaskInput("input2"), + output = TaskOutput("output2"), + lastUpdated = (currentTime - TimeUnit.MINUTES.toSeconds(5)).toInt() + ), + + Task( + id = 3L, + type = "type3", + status = "STATUS_RUNNING", + userId = "user3", + appId = "app3", + input = TaskInput("input3"), + output = TaskOutput("output3"), + lastUpdated = (currentTime - TimeUnit.HOURS.toSeconds(5)).toInt() + ), + + Task( + id = 4L, + type = "type4", + status = "STATUS_SUCCESSFUL", + userId = "user4", + appId = "app4", + input = TaskInput("input4"), + output = TaskOutput("output4"), + lastUpdated = (currentTime - TimeUnit.DAYS.toSeconds(5)).toInt() + ), + + Task( + id = 5L, + type = "type5", + status = "STATUS_SUCCESSFUL", + userId = "user5", + appId = "app5", + input = TaskInput("input5"), + output = TaskOutput("output5"), + lastUpdated = (currentTime - TimeUnit.DAYS.toSeconds(60)).toInt() + ), + + Task( + id = 6L, + type = "type7", + status = "STATUS_UNKNOWN", + userId = "user7", + appId = "app7", + input = TaskInput("input7"), + output = TaskOutput("output7"), + scheduledAt = null, + lastUpdated = null + ) + ) + + LazyColumn { + items(tasks) { + TaskStatusView( + it, + OCCapability().apply { + versionMayor = 30 + } + ) + } + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt index 865f41b..d0792f6 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt @@ -1,118 +1,120 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.task import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.nextcloud.client.assistant.taskDetail.TaskDetailBottomSheet -import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet -import com.nextcloud.utils.extensions.getRandomString +import com.nextcloud.utils.extensions.truncateWithEllipsis import com.owncloud.android.R -import com.owncloud.android.lib.resources.assistant.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskInput +import com.owncloud.android.lib.resources.assistant.v2.model.TaskOutput +import com.owncloud.android.lib.resources.status.OCCapability -@OptIn(ExperimentalFoundationApi::class) @Suppress("LongMethod", "MagicNumber") @Composable -fun TaskView( - task: Task, - showDeleteTaskAlertDialog: (Long) -> Unit -) { +fun TaskView(task: Task, capability: OCCapability, showTaskActions: () -> Unit) { var showTaskDetailBottomSheet by remember { mutableStateOf(false) } - var showMoreActionsBottomSheet by remember { mutableStateOf(false) } - Column( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .background(MaterialTheme.colorScheme.primary) - .combinedClickable(onClick = { - showTaskDetailBottomSheet = true - }, onLongClick = { - showMoreActionsBottomSheet = true - }) - .padding(start = 8.dp) - ) { - Spacer(modifier = Modifier.height(8.dp)) - - task.input?.let { - Text( - text = it, - color = Color.White, - fontSize = 18.sp - ) - } - - Spacer(modifier = Modifier.height(16.dp)) - - task.output?.let { - HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp)) - - Text( - text = it.take(100), - fontSize = 12.sp, - color = Color.White, - modifier = Modifier - .height(100.dp) - .animateContentSize( - animationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow - ) - ) - ) - } - - TaskStatus(task, foregroundColor = Color.White) - - if (showMoreActionsBottomSheet) { - val bottomSheetAction = listOf( - Triple( - R.drawable.ic_delete, - R.string.assistant_screen_task_more_actions_bottom_sheet_delete_action - ) { - showDeleteTaskAlertDialog(task.id) + Box { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(8.dp)) + .background(color = colorResource(R.color.task_container)) + .clickable { + showTaskDetailBottomSheet = true } - ) + .padding(16.dp) + ) { + Spacer(modifier = Modifier.height(8.dp)) - MoreActionsBottomSheet( - title = task.input, - actions = bottomSheetAction, - dismiss = { showMoreActionsBottomSheet = false } - ) + task.input?.input?.let { + Text( + text = it.truncateWithEllipsis(30), + color = colorResource(R.color.text_color), + fontSize = 18.sp, + textAlign = TextAlign.Left, + maxLines = 1, + fontWeight = FontWeight.Bold, + modifier = Modifier.width(300.dp) + ) + } + + Spacer(modifier = Modifier.height(12.dp)) + + task.output?.output?.let { + Text( + text = it.truncateWithEllipsis(100), + fontSize = 18.sp, + color = colorResource(R.color.text_color), + textAlign = TextAlign.Left, + modifier = Modifier + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessLow + ) + ) + ) + } + + TaskStatusView(task, capability) + + if (showTaskDetailBottomSheet) { + TaskDetailBottomSheet(task, showTaskActions = { + showTaskDetailBottomSheet = false + showTaskActions() + }) { + showTaskDetailBottomSheet = false + } + } } - if (showTaskDetailBottomSheet) { - TaskDetailBottomSheet(task) { - showTaskDetailBottomSheet = false - } + IconButton( + modifier = Modifier.align(Alignment.TopEnd), + onClick = showTaskActions + ) { + Icon( + imageVector = Icons.Filled.MoreVert, + contentDescription = "More button", + tint = colorResource(R.color.text_color) + ) } } } @@ -121,20 +123,28 @@ fun TaskView( @Preview @Composable private fun TaskViewPreview() { - val output = "Lorem".getRandomString(100) - TaskView( task = Task( 1, "Free Prompt", - 0, + "STATUS_COMPLETED", "1", "1", - "Give me text", - output, - "", - "" - ) - ) { - } + TaskInput("What about other promising tokens like"), + TaskOutput( + "Several tokens show promise for future growth in the" + + "cryptocurrency market" + ), + 1707692337, + 1707692337, + 1707692337, + 1707692337, + 1707692337 + ), + OCCapability().apply { + versionMayor = 30 + }, + showTaskActions = { + } + ) } diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt index e7148de..4438714 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt @@ -1,17 +1,15 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.taskDetail -import androidx.compose.animation.animateContentSize -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.spring import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -19,45 +17,41 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.nextcloud.client.assistant.task.TaskStatus import com.nextcloud.utils.extensions.getRandomString import com.owncloud.android.R -import com.owncloud.android.lib.resources.assistant.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.Task +import com.owncloud.android.lib.resources.assistant.v2.model.TaskInput +import com.owncloud.android.lib.resources.assistant.v2.model.TaskOutput @Suppress("LongMethod") @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable -fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) { - var showInput by remember { mutableStateOf(true) } +fun TaskDetailBottomSheet(task: Task, showTaskActions: () -> Unit, dismiss: () -> Unit) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) ModalBottomSheet( modifier = Modifier.padding(top = 32.dp), - containerColor = Color.White, - onDismissRequest = { - dismiss() - }, + containerColor = colorResource(R.color.bg_default), + onDismissRequest = { dismiss() }, sheetState = sheetState ) { LazyColumn( @@ -67,80 +61,68 @@ fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) { ) { stickyHeader { Row( - modifier = Modifier - .fillMaxWidth() - .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp)) + modifier = Modifier.fillMaxWidth() ) { - TextInputSelectButton( - Modifier.weight(1f), - R.string.assistant_task_detail_screen_input_button_title, - showInput, - onClick = { - showInput = true - } - ) + Spacer(modifier = Modifier.weight(1f)) - TextInputSelectButton( - Modifier.weight(1f), - R.string.assistant_task_detail_screen_output_button_title, - !showInput, - onClick = { - showInput = false - } - ) + IconButton(onClick = showTaskActions) { + Icon( + imageVector = Icons.Filled.MoreVert, + contentDescription = "More button", + tint = colorResource(R.color.text_color) + ) + } } } item { - Spacer(modifier = Modifier.height(16.dp)) - - Column( - modifier = Modifier - .fillMaxSize() - .background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp)) - .padding(16.dp) - ) { - Text( - text = if (showInput) { - task.input ?: "" - } else { - task.output ?: "" - }, - fontSize = 12.sp, - color = Color.Black, - modifier = Modifier - .animateContentSize( - animationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow - ) - ) - ) - } - - TaskStatus(task, foregroundColor = Color.Black) - - Spacer(modifier = Modifier.height(32.dp)) + InputOutputCard(task) } } } } @Composable -private fun TextInputSelectButton(modifier: Modifier, titleId: Int, highlightCondition: Boolean, onClick: () -> Unit) { - Button( - onClick = onClick, - shape = RoundedCornerShape(8.dp), - colors = if (highlightCondition) { - ButtonDefaults.buttonColors(containerColor = Color.White) - } else { - ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.light_grey)) - }, - modifier = modifier - .widthIn(min = 0.dp, max = 200.dp) - .padding(horizontal = 4.dp) +fun InputOutputCard(task: Task) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.Transparent, shape = RoundedCornerShape(8.dp)) ) { - Text(text = stringResource(id = titleId), color = Color.Black) + TitleDescriptionBox( + title = stringResource(R.string.assistant_task_detail_screen_input_button_title), + description = task.input?.input ?: "" + ) + + Spacer(modifier = Modifier.height(16.dp)) + + TitleDescriptionBox( + title = stringResource(R.string.assistant_task_detail_screen_output_button_title), + description = task.output?.output ?: stringResource(R.string.assistant_screen_task_output_empty_text) + ) + } +} + +@Composable +private fun TitleDescriptionBox(title: String, description: String?) { + Text( + text = title, + fontWeight = FontWeight.Bold, + fontSize = 16.sp, + color = colorResource(R.color.text_color) + ) + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .background(color = colorResource(R.color.task_container), RoundedCornerShape(8.dp)) + .padding(12.dp) + ) { + Text( + text = description ?: "", + color = colorResource(R.color.text_color) + ) } } @@ -152,14 +134,19 @@ private fun TaskDetailScreenPreview() { task = Task( 1, "Free Prompt", - 0, + null, "1", "1", - "Give me text".getRandomString(100), - "output".getRandomString(300), - "", - "" - ) + TaskInput("Give me text".getRandomString(100)), + TaskOutput("output".getRandomString(300)), + 1707692337, + 1707692337, + 1707692337, + 1707692337, + 1707692337 + ), + showTaskActions = { + } ) { } } diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt index eab2687..fa99d73 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt @@ -1,51 +1,66 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.taskTypes -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.FilledTonalButton +import android.annotation.SuppressLint +import androidx.compose.material3.ScrollableTabRow +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRowDefaults +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.owncloud.android.lib.resources.assistant.model.TaskType +import com.owncloud.android.R +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData +@SuppressLint("ResourceType") @Composable -fun TaskTypesRow(selectedTaskType: TaskType?, data: List?, selectTaskType: (TaskType) -> Unit) { - Row( - modifier = Modifier - .fillMaxWidth() - .horizontalScroll(rememberScrollState()) - ) { - data?.forEach { taskType -> - taskType.name?.let { taskTypeName -> - FilledTonalButton( - onClick = { selectTaskType(taskType) }, - colors = ButtonDefaults.buttonColors( - containerColor = if (selectedTaskType?.id == taskType.id) { - Color.Unspecified - } else { - Color.Gray - } - ) - ) { - Text(text = taskTypeName) - } +fun TaskTypesRow(selectedTaskType: TaskTypeData?, data: List, selectTaskType: (TaskTypeData) -> Unit) { + val selectedTabIndex = data.indexOfFirst { it.id == selectedTaskType?.id }.takeIf { it >= 0 } ?: 0 - Spacer(modifier = Modifier.padding(end = 8.dp)) + ScrollableTabRow( + selectedTabIndex = selectedTabIndex, + edgePadding = 0.dp, + containerColor = colorResource(R.color.actionbar_color), + indicator = { + TabRowDefaults.SecondaryIndicator( + Modifier.tabIndicatorOffset(it[selectedTabIndex]), + color = colorResource(R.color.primary) + ) + } + ) { + data.forEach { taskType -> + if (taskType.name.isNotEmpty()) { + Tab( + selected = selectedTaskType?.id == taskType.id, + onClick = { selectTaskType(taskType) }, + selectedContentColor = colorResource(R.color.text_color), + unselectedContentColor = colorResource(R.color.disabled_text), + text = { Text(text = taskType.name) } + ) } } } } + +@Composable +@Preview +private fun TaskTypesRowPreview() { + val selectedTaskType = TaskTypeData("1", "Free text to text prompt", "", emptyMap(), emptyMap()) + val taskTypes = listOf( + TaskTypeData("1", "Free text to text prompt", "", emptyMap(), emptyMap()), + TaskTypeData("2", "Extract topics", "", emptyMap(), emptyMap()), + TaskTypeData("3", "Generate Headline", "", emptyMap(), emptyMap()), + TaskTypeData("4", "Summarize", "", emptyMap(), emptyMap()) + ) + + TaskTypesRow(selectedTaskType, taskTypes) { } +} diff --git a/app/src/main/java/com/nextcloud/client/core/AsyncRunner.kt b/app/src/main/java/com/nextcloud/client/core/AsyncRunner.kt index 55b377f..156299c 100644 --- a/app/src/main/java/com/nextcloud/client/core/AsyncRunner.kt +++ b/app/src/main/java/com/nextcloud/client/core/AsyncRunner.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core diff --git a/app/src/main/java/com/nextcloud/client/core/Cancellable.kt b/app/src/main/java/com/nextcloud/client/core/Cancellable.kt index 07f6ece..330d0fe 100644 --- a/app/src/main/java/com/nextcloud/client/core/Cancellable.kt +++ b/app/src/main/java/com/nextcloud/client/core/Cancellable.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core diff --git a/app/src/main/java/com/nextcloud/client/core/Clock.kt b/app/src/main/java/com/nextcloud/client/core/Clock.kt index 744aae9..07d8c50 100644 --- a/app/src/main/java/com/nextcloud/client/core/Clock.kt +++ b/app/src/main/java/com/nextcloud/client/core/Clock.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core diff --git a/app/src/main/java/com/nextcloud/client/core/ClockImpl.kt b/app/src/main/java/com/nextcloud/client/core/ClockImpl.kt index e0c170e..71c252e 100644 --- a/app/src/main/java/com/nextcloud/client/core/ClockImpl.kt +++ b/app/src/main/java/com/nextcloud/client/core/ClockImpl.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core diff --git a/app/src/main/java/com/nextcloud/client/core/LocalBinder.kt b/app/src/main/java/com/nextcloud/client/core/LocalBinder.kt index 8d28140..d3b6a09 100644 --- a/app/src/main/java/com/nextcloud/client/core/LocalBinder.kt +++ b/app/src/main/java/com/nextcloud/client/core/LocalBinder.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core diff --git a/app/src/main/java/com/nextcloud/client/core/LocalConnection.kt b/app/src/main/java/com/nextcloud/client/core/LocalConnection.kt index 9ca3f81..5e67d24 100644 --- a/app/src/main/java/com/nextcloud/client/core/LocalConnection.kt +++ b/app/src/main/java/com/nextcloud/client/core/LocalConnection.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core @@ -19,9 +19,7 @@ import android.os.IBinder * * One can subclass it to create own service interaction API. */ -abstract class LocalConnection( - protected val context: Context -) : ServiceConnection { +abstract class LocalConnection(protected val context: Context) : ServiceConnection { private var serviceBinder: LocalBinder? = null val service: S? get() = serviceBinder?.service @@ -35,9 +33,7 @@ abstract class LocalConnection( * * @see [bind] */ - protected open fun createBindIntent(): Intent? { - return null - } + protected open fun createBindIntent(): Intent? = null /** * Bind local service. If [createBindIntent] returns null, it no-ops. diff --git a/app/src/main/java/com/nextcloud/client/core/ManualAsyncRunner.kt b/app/src/main/java/com/nextcloud/client/core/ManualAsyncRunner.kt index 60a5fd3..d301eb1 100644 --- a/app/src/main/java/com/nextcloud/client/core/ManualAsyncRunner.kt +++ b/app/src/main/java/com/nextcloud/client/core/ManualAsyncRunner.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core @@ -20,14 +20,12 @@ class ManualAsyncRunner : AsyncRunner { task: () -> T, onResult: OnResultCallback?, onError: OnErrorCallback? - ): Cancellable { - return postTask( - task = { _: OnProgressCallback, _: IsCancelled -> task.invoke() }, - onResult = onResult, - onError = onError, - onProgress = null - ) - } + ): Cancellable = postTask( + task = { _: OnProgressCallback, _: IsCancelled -> task.invoke() }, + onResult = onResult, + onError = onError, + onProgress = null + ) override fun postTask( task: TaskFunction, diff --git a/app/src/main/java/com/nextcloud/client/core/Task.kt b/app/src/main/java/com/nextcloud/client/core/Task.kt index fc2e989..6c04e3a 100644 --- a/app/src/main/java/com/nextcloud/client/core/Task.kt +++ b/app/src/main/java/com/nextcloud/client/core/Task.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.core @@ -20,7 +20,8 @@ internal class Task( private val onSuccess: OnResultCallback?, private val onError: OnErrorCallback?, private val onProgress: OnProgressCallback

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 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail.png b/app/screenshots/generic/debug/com.owncloud.android.ui.activity.UserInfoActivityIT_fullUserInfoDetail.png new file mode 100644 index 0000000000000000000000000000000000000000..db6f80f181d89d1100c7fe0c7eaee4739650fe76 GIT binary patch literal 23869 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 F{{vdV?CSsk literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled.png new file mode 100644 index 0000000000000000000000000000000000000000..b1ba5c65760d380f6cae247425754a67af6c50d0 GIT binary patch literal 12329 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 BLgD}b literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialogWithStatusDisabled_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_away.png new file mode 100644 index 0000000000000000000000000000000000000000..695e7fca10793835d5de99e2c74879f6baff0b55 GIT binary patch literal 18891 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_dnd_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun.png new file mode 100644 index 0000000000000000000000000000000000000000..74df5cc73e1b0b6977bcdac57ac9f3d33ab7c343 GIT binary patch literal 19203 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_fun_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline.png new file mode 100644 index 0000000000000000000000000000000000000000..a3db7ab8dca9277c44e686123b4301e02d0a706f GIT binary patch literal 17356 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 F{{vdV?CSsk literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_offline_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online.png new file mode 100644 index 0000000000000000000000000000000000000000..47a365dbfe413bede92121471e15d1a1b8032d94 GIT binary patch literal 17475 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 PrVMxQ=&7S`+dTaruPOzc literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testAccountChooserDialog_online_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png new file mode 100644 index 0000000000000000000000000000000000000000..8e618f1c30f1a76da4ec8d6326ac966827ebf374 GIT binary patch literal 23286 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithOneAction.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithOneAction.png new file mode 100644 index 0000000000000000000000000000000000000000..6c51e11ace51278c5884fb8694ca2f1c02c4af8f GIT binary patch literal 5824 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeActionRTL.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testConfirmationDialogWithThreeActionRTL.png new file mode 100644 index 0000000000000000000000000000000000000000..8366a9d6a932eeec21fdf840f1542270e106e7e2 GIT binary patch literal 5313 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⪙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 W)!Fg)6R6FS~ZB4B4Nzz6PIMnMuia zT`2s<7DE>E{F{pMz%zvm2BUPOhulLp3^6>S;C?>+`0>=Aj_jtTb|qM=%Y{?ItmUqR zQx5paPv8$t#H7~zR(0|mrIO-V#H{JlfBUBD!6Ons7K?V}7hrPe2-0>@gdiWcTYBsN zoxg3>sixBNFQz}F%Gh58+r6M3188UisMh~CP>_953Qv}?Pfdb9`!moWJpOmhc22Zs zAGQDEI{3#L1RJp3Rq#K87t#wV$V!j056HeyK>u(8pfVN`l9>+*VkZ$2#$smUAtIt= zt$0LRt8kI`8$9x(=l3SMWNwoBt*>@1=>q#Vc(QFDydeH_C8OZOcJudc1n~QsC|%j| z6psZuYRT{SK$R-UrV_;m(0RrEE1Yc%y%xbu*oP4T{y2~%eFDN`X^xn%!G6-np&V#t z5qz2P^?j^ooOnXXgMs>)U@nyi+z zcLozzI<|WA8IQOIPPPCsCeoMWGCTm9Ey4=o!mH7=PtVb%3l3wY%-Rk^HQp4Z;`R9S zmvVaj?N6!eIk)(W#Q3FSwx#lMMFk-pf^K18Dt5-f0PxZZ9{K|Q3%Jb&Jd5!6#SQ|Un3-F}Mn@$=XUS&fdJJSMoUyT0nBWrt z<6_D^gnY?H$vRX#LRs`4PYS0Y=!NK|DK6YyO;B1(dD;PZxl)n3`ct9&{M=p-Sx(iH0mK$-os z$BtqmBqS@7^2mJlB!TH7M9$0E($iO0-{J0x8-{6b;|t`7RPzt{$lARLY}H;C=FUQ* zDxTVr@k~thm&ppzG8KEOU<3IG!9)d=KHlV^*eqz4@KB4zY1{I$skWeG+j_aJd74=) z>_B~TN22eLTNkcwGau_`4~VVwDhkM$ z$m>G2y=Pf(;$v0s)<@3u4liVCI_X}RXIvqD+yc3-bWFF`8>054EQD|ODb}6x7WcoU zJJfxU4(Uc1cn~aFHeUM-af@0XF}NlIihw8~ic>MxJJm7&rjNr(T17Puu#;bBdTjJb z5McmurI%08ns3Qf!CCPfOYA;M9C{^B$u9t%F?@QpRnPPrWDAHY4&`%Oxs$4BGC9=C zy}6U)OH)z7Qb#scrzH+BXfU#PaLF^OTPvIJW8)qt#JIt1zAZ4WfzT$G1rK5S5Wlzx z8V)x-Wllq=uN4v32t{`Xs?sf{`T2Dn`8kqxEy*hq@E#K(=&EMD-1x0(-;iY(rITvS zCXWwtlg*OuvG1;+zWUlIM5qO)+p<%(Yb_GpI|epOkMzOx+H26TnDqLMmYK>-E5_RS z;CRj{AL8a$9ELAhodUk3w%u9SQe@h`^hLjmuY1?Qbj~*}Y2;t?GG+BWDdUHC|E9|& zaq0Tn`nbElpAaI&i5N!!;m&=q3%<;bg*xo z7Qo$WW3$9L3t(GMJpH|AsO)cSvWh@}Q~l}-*Vh!mI>;_}S9mS)Yp$_{WY4=ygmT2C zdho_u{A23S03B>@1PGtM?78uXx>P-wO#C$l(p%h1+cE?lC{CnBfA(#_e4xrj{7b!4 zP`kx_N%%SI_9nx5mv(?*MfluswU&wvAR>9HLgN0nz zB$}Ljef_5lLrHG;DIiuGrB`?eoxAH4Wi4u5OmjiCD~5Xn8Y(GR@#Z7cZs2mY1Wf3v zgKl6QaAClcf$|Z2yVvQReDo6~|+A0CI`cuwes;q3*-Y4#`R9qw$f)iJ8hOtG}Dp3qzBM12wD>`M8TKu z#~KXxjwrKFc1>3YC;P%sUks3=t8l#V=Z_2Fksf*m#VX{&ZFI^qRBfH*N@g3 zka8AS&QX2-KDlsb)_213@9h@^e_hbZKBV=4AdP=KJ_t-DLhy*6H7; z^^8V83u_e~eY8I?g8WCf7J{Mb3&=H*ZEBiTD@7Z8n=&_)^QQbMsfv0*w~< zC(eGNy3=W0C$}`qN93Et)0b5(75Km$LPNO0)6fX8uZ>rMfjnAJt`efJ=66WuhnK~e z1`9rL+`wr*Z6Un-{$I2cBT{eGlMz!hyq{<`=h*;PgqUrjN8mX7CtTr~qzJO$r|O+p zKK}FLwCw!VlNV0xk9!NwsnBQn?aevFSG)3be3QC6XOZrr8h;d^fKFHfqkARVF(80k zszpdgwejtPU9gi-EloU$g}Rj%<-4h|e>B~hQ)n3E8h=Co%{8ob>WA{Q&PRPD?SQ@l z+huJ9#E~a*PpEp#j9Juj9zIs#!rR_Xpvu@iM8wK<+-CmC&u)S^r8(U}V5O;^EAp40 z_l9QS(BXUTtDf;%(fHDY%`vowo`0L$oRW+m^4<|dl0F6Bqx$zkjC0 zKXCCsp~`3fFD3qgi`qLFc>rxm`}~A?wRpE|tiD~Ut^1UR#QXaYbM-~XAmDi|Ce~~A zK$^@>yTUHm0>@FnA5dv78-iIm(V?78D^VB7G+qm!L4ZM1Y~`M{1c@f~1$C`KLjpQNP1k#k} zaDVCe5m{qzsI=c~RSJz6`Nm6C{w{S^>NoaUyME9|0O}vIV&>EZ{C&)(`MdqaI8&3L z*`iw9LRq{2!9KUT>4K!zY=de?mHbC%=3fmlI{3IC9w!Aj*vC#`I^}LgnTyxw1Pz;_ zCJfXeQf`o=lmJ%IyNZNlx%ar#SBa^U#a3O@=fE68g+m+#S)kw5f(fsOc8W6{bt zR|kn>WVmzu>Xoi8J2sNJcQN-n48?1Cj;C*2ksV4XM}L^Fj<1S zl>i;TBmyUeGY&adRMdo}YKpB4^ntazWj?e&fAw&&;wcZh?9+K9<577SUOLh- z{}}1aihhHYuIKX)zkLn(iBMvcs=B*%xz~S0JK^GH=kZo(+`Z!M;cwNp71}OJk-bGv z;ZhC!|61w)&@2BVE&cbM^j|x4#7x)L%X^^s{Ci_T&bsa)4t?3dj%iAu@+WC+V=d^G zI#)-&GG7gS43H-E)jbJc^?8)Ck0w*xE=;xhkWY?2<6B^A5|>695g_lg*~3jdyrWAxi0&XlD2bIq6Wg%4l}x7*Hlx zI$qG^><1;*CXP7P6 z-BqjbYon{GPd;kN(hkYKNUYwFrycOwNlLBp^(y8EO;to3OIp-cn`^BeY?yDX&$q&# zO^1571u=Ikq2YtndLSOgY#RvhoY_dA8wSs2n_6)^s`A&Kr~fo%DC7pmiJve6CL5p3 z&<#UpBW&ED@vVjlmUSV;f3pL}u$Ri&WaE(d8xyqL#$`Ihu@4#bOI7Rbo0Dfyj9K(V zVxlm2Y$ozUI(7GL7M5R@NooV}z8f9qDX;y~wo>apk^`*`;qpMC)7f$BZ5A`zzhi>2 zgB-t)0iPa4Ay&-lJTV=8pkagdvt~T=gqvHu*IE@e`z=oFYhPb9_M3d8$W)yvaXu0+ zC@iDU4er|I>~Awsi|W4~APg*y?4hO7PXT6513to_y(&v4O5VAqxDB_eggHVZHO96y zi^#h>D=#OzdJD}IDe0U@V{x#THH^UrUV=it{>~zhi#DPK^K^^P&7!6xJBQ7iQ&SjHyAG>siapC)i zbrUa5JB0TpIRn&dkl;Hb<|V_6FSrdC_$vEu9tx}K{ID7GWkXT^n7 zni!iVg_vPpbNhOyOaU*wQd%*}jYx>2qrqEqtVpr&W5BY5(Xc~Z|D`mg8j2tIh9~MX zzHa114kXLToLk2zLE`;35HNI$Bm7pwTp}9EaIsY;i^pKT%TY7^VKG)+z+>z z)413!4j>HS9Q$O&&0p6h#s`|&qo3nds-2ee+*H~5?O-J3H0R;)N^tzzT>L~Y3~jHQ z0d5p*754`K&IJD5*=J+BB%(W8ZN1L%b>0a8b%@%alyCY??pI}_oOudN9f;$1e4A6v zDsn_<77t#Zy$6nN5@(I4_t@Mf7RAM6bb~1GW)`B%r%pIvP60AC4f^F;YV;01Ap%e# z=CpTx(aFTX(EeTsA9wTaR^04j2qfSXpfE@@kLGr@ZZL*wP#@^2QI&VT{SI!=LP6a| zR0}MUJ|^1^01bj?5drr{wY5wM^P7vjoOhS{#4GfGm3<&2L>rppwgEP+Q4T}i^XfRJ zFB$k0u=4EKz+9)=kjaLy3*6arIBaZ-_J%1knOGbRKtm-} zvF&mFf=NaG zWa4Yz&q678(~If-KG~q1o8$>`K?w!9Jb89Zi5duPp9qii?<9dF$H>;=}@KbjC1IeU(H`q~Uao_o=9 zP~Z{UA38E-qQb>T&pwbbxP$Cw08|~)YzDLtC{}O0xS?H!3ZO65L zL;<>vu8lqdYRa;diwsCZha;~8?xfnYO$q9YZg)^cT3@&;wr|Cev4HGe35*1lPNWr= z8;VTAtqyAVO`^J~_}1kcM4#@0)RKa1z1e4pUPf(=vkx4A&Qc;B`aa}l;Rz`)2@Eh7 z^dqUsTgqv-ZKtos+!V?; zE=UevD?#nO1h$Kw-qZ-G+kv>-+xv9_mL@+8@XI{Vv+>?sLvQzu&&+b?^P_^uHn`D=TY# zKF{-ho^}0@hwEC6%^ENmZ0-Jie>)6=t&qZCD}Vfa75Is+$H5R7%#pbNZ$BQ1?-vbT zeR|}mk;Rk(xnl2i?Gtr=7uNk~@TtLPS5Ew`?f#wb&A!v#Z)&V@%5d+S^71=6U;ema z@0)#Dk2dsL(_`p^T}eUk-48zCDhr0AWz&O-{FO9nFRD?ARJ;D|fByTQGw?rW;Q#s= zFqN<_o2-D|LL_3Vf8)s#jU$WjTm@o z*}&?Y^Q*y&|NeUa${RulagVM1`j7Yj^O*kr8qjh6beeZKtS4dAq-RIyjw z^U+9p`SY6b5vhE%` zyp9bg2;W9?&BX78lg3%Je@n&F%aEz%eSn+5noDIiZ1PxU%c8$ ztkhR$HGYdWcPoyh@3_lRP`X8k!Op#V5FRWKoP}thR`5$^Q56GCxJgdJ%3E=THw*FK zt=+uSCT6DBajoHbFZ}2zM}lS6V)L~I)F&o5GfUmY3B>5pkCmPI$0hoMvMV+IoqM#L zZpi0F*>v6{sc}nE-N#_)h>28ZB$-oR5y7pkh?(rIe=y9d8?5jyOBLPUPTFYYfZ5RD zG&$8>9n4&s+rRB@7HvT~iC1L)U|vg9GNMws0;$8c2tRtwUMBk&T+~oI>$nRhYwo+)$Ty`!I` z59d2Zp~h7)$Ou!N;n#VQPE#hb`|MX4TO&;kxhh9`**J@S48MKo>!!(wcXf_P)nsN; z=*znOCCu8$q2v7m+8}+@dXxBAn~Sz$Kr#73I_pLn73tVr*o)oeD?}4&ZIH2JIj2iS z^d-L;@%?+jcCGE@`JNwwBaiC@{t*3bONRV8Pi zS{r0atxKMs`mE%5%B=Uwl(O&KeS|O#>b!oaBlTF^R#K4J4s26Vb8bql@CWQ*9Q5am@0@*(9w@ zLf?Ix3;b$!VV*po|JljOlNAgT*~Sn-)LUy_Zdu=CDkEGRzdhcpU_*#u+M84ftNNq) z0Rs1SplkH4gs2qM!Aj<&wKU%HQt~8xCTm*%`x7T6Y@O}2BDq=xT2`S8k=p~-yIB8`ZYA1qpFEajK6(>@Eb+^m+Tm1OwQ z&W2o8WPTPVjt@BOlaJBDR=le|CKME^$%*JG|K;B=Wv6vk`ai$=urA-+B92>=G-K&2 zv<^Ui!_(umnnlCXhI0d8agTK58-khEex;0(&?JTQ@v&Q|I6QS}el&Zn+g?%vFEJ2k1tM)crUHfSzPI0@h+78o!Z%D(N=JL@pQ{*PZ)cBg3_-=uD8 zO04%?f*CPu#pK(YpqNDF|^0`>w+&HJNPS;FMeBL>1eb{YxCiT zjs#I|Mu{ly>|mhWn6vOxZQTQNm-m);0~3e+(w8b7uh<~b4)f#p5h}K`7KXm7Qi`vh zQA{U&9hlG_v)IQ!?2xkV(5n~NE`Kb+(n(fMHiE^nb%KGDIUba<{ZMO~fB>MS3x{cw{lKiN%Wk?EpPvXnL zkQgw~QTv;uVOUu7Nhm>mAEPJR$%f(?o!v#`6MGdmzMXvfYca}QxhjJt=-88JDAOUr5oSD2 z)w?^bONi6zZyjep-YNDGO2#O8=s3bO*^i;AhZiY0cr&iYCBXVb@Pv6}j z-QIPqx96<)9r0LOH0Q2tQJXSd=8l8&n?3enXyVv@YE<%gHOoo$gI>g1k*id$p7Py@}J%c+6g3kvS>evK7VQ`j^Z+4!IPz8d`l`=4uy2)Vid13 z{wBCgCyfyk3wEhEakA_CqQsDd#J~YKw>t@CuKK$8>jq_sKOQU}2fV}L$jvBh7G{Is z61OHiTb|F^HVE#oMOM5A?zrz$_j+TVn+0uW_CPsz`gMsf#YEUy;$}yZ;l@=8m$DTr zSCy_awA+byl3$kTm8`cOxFd_2JIWau-ahn8V%z21je8X{yH2?@ z#7!1h)V+TxeXi6R3!aSeAa#lv`YARvf0vP!Z}ro?MDYP&%-4`eu*f! zck8@(^mLli6{te@<5IEKq61DlCjF&87Yyo%Aa1;ex%%tk=oZcGrPqt!bJUdjBbU6s zi76{>E$ga$SDNcg92eIG#$EZkw1=-+c=~M$%1LuQb7S%J#t7sNT-bQE{Y;(x;5p^B z&b(i|Jso!u)BVpoh#j45#p9_Fxn|OK{n}A(3~77(Iq56h#h^sTc=j(#SH5wXW$>}- zrGqO{P^0(jj3+kYFahdtDw9u(-(FiWw-;;KW6(9?MX7FZY^s01v8EZv9X5#E6XJ)0 zZVC4yglaY~F<~?g>2p?($W%T(-4X6pfs!{DpVrjlhuGW8P4z;aKHLROq%P(C<_DuTHmU zIyPxYj8(?13m0;SRllDOReH|F21RD!D5Rf=>JL|q+A+6NnZxw;U_5&0Rds$rWT zuK>#1Yeyy6&0qU41)DB2T9+^Ro~l?x>0p{$XA8p1%&2m-M^r7hPpzoF#qTOe*n)nk zyZ5WDJzR|8(cDa;&Y)~iv3oBxj{>o{<85DQXGiH&zAipjEF4yCWHb|U<%#pn9Ysvr zk9FH0^?6Rky?;*6)_u{Fv~ojsb`O=F%daS9>&sF6({K(`Pmw86i1UXH2-=C*dVd?G zv`eyUQneDCNz*3vcQwY-cl;3O1zj}nh*c1&C!D3#Gmj>;7tg#JES=Vn+H4Dq`^g}i zZg^e&_JCzlpz7ieSBLi*cn0&r2O`6G*eS8&vB7o5Qr(S&SI-jHQ7hiYIp}f&Y=aum zt6&DJ?9isptapce=2J4LO!zGlChho*u`AdY{0U2a2+@aaplZT&5LcUK~KzhArIGbS&*i zJCCZF&EA7YnS0O?N;!8YrlaxQQvFN`0_mSWKV^6B2=N1u4=qfu0h;tiYiBukMw)E^ zcK^UHCvWB%j$^*bYi{#BlW(Fe6R0u*YnL41TB~2@*i*)~A;@ zl%UK5^OhEr#RJmjS-4_I*Aan|0Q~^D9_2@eA7UeW1nqhDg+_{pqt1wf8?8-<1LU&< zD6G08BB67VM9pb-a|m8$=NGn>fsqLV*NB-5r}~oekZe86yviGC zq~UnLrUln)uTHr9TWouCH`QMj{o*sd($&++-+R85*_HWP%t6W{@VvNW(Y83UajRaU zgWy#PyzHijok>RBhi2(Qu;+uQgu8i#zLfQ6%AZdT5l%NxIRu|;eeK_+lx297<)1&$ zAb%L%2`yPIuw>Pq)#c_EZsC!6%|=9l71`Zf2l$07;f|YSKKYZhrQc5CQXP_6Tv;jj zZf=*3>F1uOpI#5+yp}jc@DhWYZ^3>Mvp1 z4bB)~dMnBba?9%YlhC62Dk+XIx=RMEq0QRHrPNcbBBnMjYq2?o{=g!z;Rwa+Oci^z z*5(_oYRf%`2J|#%4mJYgrt4X6?c2-9O7PYcgjKNl4sltMX9Cn~GX3~(-|!;$v5Xw` z&85a4xRLdao{e$S1GaO*p{+#rHssvFo)p^H)tK?$+z>3qG?$FVhjorfTT2a9>%pA3 zjYtizViU<=8{ReiOb$%WtO$~MWIuhG=Kaw zxty(O*+7(x?^&hge!2j6iY9`8a>1Dt_s-8-vmY$wpe%Ga+@k9cSWNQ(nRICOwXm`| zj%BE`3dKeB&SS!AXQxQS9T|b~ZP@92tV>Udks^Mf$W>xYjbGvzDh^NfKJMaT+Lym3 z^3>&!TvKW1>ei(=c+LEt^x^cQ0?QJk!d|Pm`Aa*aU+*1<6nAU(<41i~7DvN@g~|o^ zp}#sN&gd?4QDWkW%!3o(CThn&I!j!SOK0;Fac7_6M?J?CI>-^NU#>?C|5g{K;W+et z)BU=QqiV{jJt#xCnJ^gcrl&v-tuU6_d2|yA8N=Hz1Bt{b;cB{b?GrJh4muSO>1&>^*B4gOaTb@iQ%eE2z&p$1v}@gjbEIQ)qU~!*b7v^ zX(O4*d06?cf2WdvdAt9u;j8%ZPm1~fNjCqiEth~gTb8Nx=1f%2*X}@7B-5QYjKR^OlV=dMRBFEqW%&m!Q7jMSW&KD zP1r39`q^XsPh!*PXJ7{K6co9QPg{`08%ypl&rK|E;uf;xX+y!SD%!;696q0U&+tl{WjKU&}25HtBKnKe_Qh>hglL?w&bcg~Bk<2}AZugPi5p&U#5#|yDX z<&ldMQb_QPZ!3E++UnEGooOhvD$y&W=Qn5E{beYTFM5_BP^B_?gm9lB)-Zy;e*+Vl zQ;@$9O}m>s&m--Jh$2tMC_{6&ao)_;&)4_~w>jrn`w9UUbYDHM+QCCLo(i5AdYsVk z{*q?bN*VGFUR@J60gT1r(0MQd)*39xmIPGUK%s0C=JWe^(vk>&n3-jdWAulgkq}za zWK&N&I%G`AOrz{#H`$ZdUFx2a&_2c+U`G`xytrL0x+7Uv=McYhk_yZ&c{@X*Fztq1dYyN@I! z!t!^g7{S+VfPWQrKMS{bzfQtZ`|FNwD(2ZFqtYX-F@3jZ65>=ZQw2^o@(&Jv78;JF zFI(YsY?J$VLpX^f%k}E@8q?<}Z!|=Y-zdm&sJYM9?`IwLMFnDc-KPsY%L)nj9_Tb5 z5x&CQiFLk(^AdUuKOM@+%fLy+4q=%lQ$cwEExt~9_X}XfR(?WjBBkX1wctYX;Q54t zSmLG&@g5YPd_BxVb}%xAx3TGUqj#CdQ8+kZhl=#fIG$NQV9vBS>b_vq3uedXp-DSx zbGmm)%AcswNgc*iv4f8odCz*aS=u2GA77bE!!orlY@O`(PG=P9huBr4eo2P{pIzN5ot%9%8_0JB-4sf=G_-`Qg>-RQ2h z@%!(g?OEcS?)IXej43?D%FouLF@)9~o}M`QyN9kqEX|={1E)TjK77?d^UH6MCkGz9 zGX4E1>exit515JHYCLt53R>qOBI?qG6~$+r8h{IE{QA-C$GhUzIa-`F(;Z}7vrBdG zdB_7J@0Epg4EhiYmqW@CrPchcCuKky?PC$wCm*D9l)H0+M;D+;@x1W3&|Qh{UYCMi zPBRWcv>z#s zdH2Nbx;4%KI2lX#n2ZG4uPZfkJqVE6p~@5#Y7AUCK2K9<(n(Ag#dfANW0DXWod>o> z2RHHQPbu_xm;rKm=)^wOl>s- zJrxh+Q)1YO4+(JScJ=hPo;iCCDW=2|&PtlYF+B-8x&|gG%HBuMW*gyM*@fmF8=Bn@ zc)I82)6n8Q<6^BL^pP0_?>B71kN}XlrIo7|o@!7lejtT-2|4JvJq>Ew+fZO|IA|qr zW=SgCN-QzEvX*2lH68_8=z#=Zm|bXp;`#ILqCHm=J)DdRK~B>Rc#FeJM1kbPOQJW> z2yUZw|4}T?slsQv0r$?hjgKPbBohY{eiDE8)9d#|VpQV2?0e(M$K0FNL^a`L1lzH8|WcBA>-p795gJ^C#>%}hgiBMs4>Qh%Cr zF;VfJmRyn-X^_(@em;~uwjPr%FYaq)F_`+7hhr0y7DHam&MyimNtlWLnsA6~ts>Uc zbGBkLOx$#SYeR%6Ri?d)^RTAd`~mfxom;`#IO0=@TuLZsg$kEd|~blT@#G z3hw%*%fm7J1nS%vcZvVm!VV2;{|}^56PfXo1-q7V?e zq4}m9&iNv};uf2$M&7NK4MVC{5)K~^LYc+tjdyiZj7IesI)Y!!2q?AT$z}f1N=5=V zQ4pK?cD5{yR(>ZkQ!-E;u?IgdJU(Mnnbba}L5&}5P=t<8?mQygJ5B;Ccs!AisP-DO z^j@#2mF9(nxmL6Fl9u-LbWPO+2$c%0*XWs{dSszL0pu$Wd{G3)ySTBOP~J3eHaMwc z(%V#ezpjO4autvlpY+IJJG+j(u4|fKCv-=|b(_&Q_B7j0p2tQ~r?EbY+;$x~P!7B5 z3UAXq_M{kTjs)fHLOwDxUr4jItiCXPI}hU?$BQ8)*TIr|vl6oKy98~-tIk?lNa>jZ z6i}s)hAgHL2ry(aoWg&5oa^R$gyvY1eN^u7l2P6wj9oQ8$OuS)V%~i ziuWHM+p!~txo@$?SM7!Ak%L7>l7sW#n(TD4rHeJ@wluseRS`*#IpP$tUXi{@Q+;kl zW|7uy+X78T3#w@;t=0EhwjTak>$4!dQ^!SS)~sG3;jUfWbpG_l7+;1eSZ+HX)4b{Q z$Zravv#P#jj*#k9bL$@TA+Pzf+0j=?~G{ zG#qJVHn+Z1T+>++yPRw*Ci+r*DA!GB>Yx>dzAOEYzuZ~aYlyGh&8C>UVFBS6# z;<1ZACn<9DeQ-D6EoOFr@=+NLI>t%lfXTN6nH=9Badw zKSGS8S2JmPX>@$)#fHN~Lhykr0hT4wc>0h0uKUE~k7=+A!8PpS^0cq$09mGT8CDFD zA7btg8E&12lu@Ms0e69{C9@)!@(1m3FeU8*0s@5ITI%PpFe*FPl)5p;GeLY4{~2n4 zz<*4Vi27=8m+^{?|3vo7vm1>2Zjg0LJJU#}(XcQwySS;i9JBf8@iX8WX|jq*qw8u? zlhrFqxR3;H2ck5LTN?q!B?$X>N9f?IVO1ONa_%0$l(W++78*v;tza*owLA}PB8HM% zJ9Pjw2V6EQ7V4IvMRSbW-*{n8&#i0Vou+B6dWw&DnmV^eJ@{R128s7yXn8Atp}V2$ z$wAM&+2DUnK&TPGHCwp@HFXw%0ByV7=6d)?C{iP=>W&MzVv?xNi0SZ0LU893e>mIo z@F>(hNS&Sh4_U;RiQGmDPGsfmLK3H1SuYB?lLHR7&;yxF54-UPhsCbAb$=K3#1c>t z2o-1ZcKQk14P6(`ePAxexD1>N#xGTGJHV`t;ufUNtz?W}X`E@WR=)!s2I#1C@r$;S zV=!MrXg|o)#uUMf1g`-Ghv;NUzRwQ+Z0T9HlVhZY66~wd^Mq5g-JMC<0g0iTe~3R* z@gz78T47LWp=<(zAgv2sd(lgD>jON}o4xvq;~>3Eih!1 zgST1^-Dg0yVSwd9TN5NgXuJmnm1h;v38o!Lxa~&nLtC>8@&H_gY^eb4RBz&}Z{=Aqn>x^icn_%uKx(WpX?gr|g7}p$qUS?yS{<*$f-!?ysNi-C^$AH1t zFg6k11@RIYBMYw`8hV9KA6*=2Dl?nM@vr4|0+P-Oi@BVF5@8<& zKD8YDD_4jK`q^o*;`A&ZJ47q822AyyizYQU>iuZR(4m~{Z2aSlPJJ_Jospvk>5db^ z<@uFACyJuUoL-Q4xiu(t(YS@*wnHz60cM{TP(<+qb zfXd0%NNU%boShdZnMPmOaN=haYQp^%?y<#<@Nzak;C#M+X39?t+i@H8AP|+QQ4?D_ z_Pbg(U#kO*y+X9{Iy$LmSBVI+(hVQv>nlJegv$daBP?^P&lr=VIYvceZHG3U&i4Xw ztPy{SFsO8}I`znTwarjR5&4KEz-4P{jtHye3J?y?mRtK2GHs{3JnSD2jRs?nAJ2N< zDA5~hZ4SEVfABO9#1W~YX<$nO$}yCApK)4U)As2!rs)u@<_Yw{hTR#U4)i)r49U(0 zpm$~`^#rm#kFC1QLoaZJl?jo|4#h-v)7s6%2QVGBfGV2@CDDlE)7x?#oFO7i>0L;-}Yh!njV#`1L z@Bg1M`meXhKP{vE1G4FkEkgW*|G8Oo&8-fp}R@E#qom188@oB-@YcatMvW_eqv&_MPMh6%5szO ztnJnU2OcA2Zz+6*P#UNeb|135J@x~H+8#o9!^j?UhR1LXLqUs4$yg2iFSXhkSvh$e z_h~>R(B=oYiSy#@`XkwiHQU?oQopW=;95bHX9zCm9 zJOP0f$bu9cMkE9G@#eax?|?Pn6ah_^N8#R5am-@g&rg0IwNH(u4GLgHL9qn>0$}8@ z%;6cpqo_K9wK{mOUdtQkC4fOEZCEIpdsg%S7>uAUbC0b~+~!We8xm0k8gl9qjd8z|4Aq(1qY2n|OcayIfb z(72^HJ)LX|J&6%q05u@46O;#-(wh)!Y`e>Hn=U`kg^J(D$p${z^+)DtP>fFYEru18 z&N3*$lhXmypz-DJC4@kF3E=&dG3XtOleL__KcO8w)hx4E?(uVmGYT8i>?Og_s zR<}?iiY$(xGBDW)m)a zGuUsPQNbqz7t8H@YM91OE)(MTgFIf`%u1cD$}1X-+a z83yl1PDBkT)@~-Tl_j6PKL*_O?ESX1sEML_AVDAzyGD_=FrPQmB`>sAxUG3!y<-%W zWeYoZDe&qaAm;zcy!lh=B28mOHg^j0fBPOnFS=+&3T$_Vj{L!!87S2|a!s*x7>0z0%d#i&aZ)9UcIH@_oe`M_eUWFMu-8S=X;6Vy3Q^7l6(t@jkPHKV#w z31Bo-xXO%F&S0h>hlanM?0W4*y6rODgS}WtCjf$mw@zp*E-GsjMo|~r646W|m<%{;m zqO(;0n7rv=KKW;4dBl2Ej7$L#PAIq&P675%%3fh}K4pj^CigLr8&F3T8eiiV74U=5yqW_6TSa$q*6MXj2`b4@!n7x_$NL`pldEjtbO5uw z8)HSY+Umi$!94ZUe0C(%x>aMdi&FRzc_Jy+WZ z6`vro@kxV2W%XZZ<1n}tXZji%pRuR^P=-hPnd~ikqft^ttok;FX)AX_)I+_+XmIgQ z0h>;WBu0$`T-H)NX8Uw3+lWUw=}%J+F5W7y=k)g|k9K6PceXe!5kC6C;a zh-326L{YZ6(xYhQN2YmdWx^)EfLx#S0av54wBexcC6Jau?Xn3+-L6S2!I75vRpt+w zUJ2X7%=84kl_t_U2aEaj-f|7Y3&r?%e%eU^4}H>s4Xx#eZdP$IG}gC@7mBnA$Jo|( z@)+YhV{x|5N}2P<+pP9_)cfDLt`Rp@$SxXk0_6)IsE}DKakjLhkE_)ua_(Fqy7gyX zRdfBa#67-&HzC9B%G2+S+z-(l6k)su0{Bp9e1O|r5{A9boyw8Sz9esi0J9P)@V>uy^$W4C*nc@aCdh(LpfPGiS`y57uh(E?9ZBGe-CW*-r(327A8d zIJjlT#y}Ro3*^;*XraGI?SHp$@UOQNke@FT>NQ?MM|no*FOUo>2G|wn+lu%Mvf@CP z1WqJ(&oguXnIm2oiq1xzKfXCeaaVQz6VUChHGcm`Cbb{B^B_SGGtq5Al@3=X#}|?0 z1b2LFOB6I?lqstKXFOX(mc|0d20HNv$jgOw(AR@NsC21&Jo!u!^U-clneVI;B`p8G zhH{@S{v;T2xDhB9NL9rBr;IAkRN7n%66?4@oTr6UuauevVa|uBm5|)=KeP-4TVs3e zqky{l55uC(TT1O1KWaMAkTTRgPjs3+)-W>cCJ9e(5M>5@?4hiJ7B(655FWiWMptj6GdG<1>N^_lD)Lufk#ga|LAm!;p0q8(zRj`TDd1g?No}eN)dH_5g zK>0jE$;z9I&@Z0D25%dAnIYnWjzJ->FC@G@f-d7+4h3$q#EtV1w|v^*TP#1LofO@E zh?Gz(M9EzB%(GBsFCMP$@8p0?1hlo!B&Mkm;bH^&moS?v8cCq!Vs1LuXH~$5x-Ey6 zpF#Nt+5hJn{&x?q3~GJy-y}|dfB5A;`xtmG!>zYWvf6VWxTz`*oYiNEH9^W-&IVcq z9RqxB1IUIA_N1V)r0^O?s8a;aJ>-VmBatG8XV~o3x<4|O+Z6lclYX_*#%F-Br;4CU znIft_*XGzD+vn+#*pK`KG{HwSsXngbP$?H&e#@IZ7AkUrh2Hh<^xeBaAtYlCL!y2V zFNwH1=T}KX-z>O08_-V|#HFkQL$Pz&lcXwwq6yG*Bv9$(cjo~}eklM4iYF+uQ!prC zj*Z||f*lNQkiL>IW>gV`kH=<{L_NtPT)@A-PWnwK>EzF}-MQ2=!@<+&OW#2#B(#um z_T@-f!5dkUc$_mgPXz=>Di6nuB{9RBM5URw^Y(K&0?A}sp#nvrbEr#p?J|_ zDR>G)2RZqtD?-v@BpnYkiz=A@a>YuUlNwYXR*|)j&@wf1Hsi*1E;QDLYAHwlt{GU0 zGoS-&!MuVJymd8lgJj2$04m&m2A8Qp?RerpaIWRfEso}ddWxn?@)AX1^ZI2v!`gcs z)U*WnKJYAoJ7}oB8P!!}T7L~@z&G$HEafC<>sKU@r^hd8S{z({utv!ls^HjCH=EBH zvurZ3pzR~6&OhH`GComRwSTotX@)c^=?6e@=Na~UDG&KF~G@e=`MDiEL`tP%c(yj`RyDv`WF7}(d{qOZZracSl=B~2-r{*DX^VT%qG>#`#aCzfNDR# zCU&aNl66R1u^HFZUcc1B*$_Z@_oS0BtQZG=OO>-Ln&S1~uOboaLn}*Hz!jH%00B0_ z(Djk!0+(m;dK8sNhswFbYxhJk*#rEmvx=4F#uH{LE zGqE$3YerX8zZh4&+aWj_C>$jPiU|6q(mv0@!)aa-2d^_ZSPT34x68?UJSCiPEZd?{ zI!keH`?AS)5^W_a??Vp;M@;-U)tb+x9)q_xKBid~CdJF|lrO|;RW;OE@GN`_qNO@XIWkc1?S@Ks} zj=8SmjHK#Ij=J|4rV1+Es7)}HR=K9d_dWAS@*W;0ig~Er(lq*cj8H@Qi{=NwS4W(c zfb5pIk%Hv5fR-%|)wON6SU)*XAJq`t9cbRLjyfIg`xN>lUuIuqfbMDoac_v_Y(`X< zcLAYxsM_Uv^ZmNM(~mreDG}8{w8R%D4Z_p==<>wz}9o3l^pWLN1_vbLq?WwmabCjJ@{;OrwSKJD)(?3t1s0;$c|dKOUa^tGs$4_?3F~ anOD}jqnsy`;0bNm{=FW5t8zYZ>3;!Mbk67i literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..126a08fd1daf0f4986820d239473ffed02d72532 GIT binary patch literal 1458 zcmZ{k2~ZMP7=Yc@+S1Czb!&6eP_YFyS1EQCk3>>H(;~F;E)T*ZPgJzB#Wu%GQUo;5 zQ6W=7Gt^Q8J6tO>9Z`c&D^oHg!xTj$*2QUdXJ?vyGw+!H|K9)Jyf^dZ9QO9uqO(&+ zOG|4D8imAaW|pSMZ``06GetMuwY1ig&`4KYns~NM|9 z(EtFO20zZZg+v0l@g=tAy8-LgKP?_X8QAW%K%Wi|9?Cgtpx<6UQtSLG#aJ&p=|XnW zZwVw`n=-9m=~!fvHoHLL4@j&6)(-rt1%FHqytr+8H^>#kG)JNH0pLwo027HJ)3m__ z)ZL&}?SEEZ0UBBZXlQd(j)vZZ-5UfR6*pm?cs!f;JD(NKJ7Q@GSSo&Ziy}?!==V}8 zC-{-(ZWtA5Idtst5&}~Pnn)fb5{<@FAV6Y}CKFIsC%Xx%f!jyVz@2n*XedI1gTrln zT#YIbcL)zG0D$qdr$rw~Y)YNf5bHO1s^5s{G##7l^@r#2)n-*J6vB!>HpEOYck3ls zGT}hqp0|^{HZgyXy=H4HXkoDgEjUdqje1~5fBj)8d#!#)yUt{;C(J)WL!1+|wIb3x zLxkupSW^jGGB^V(WyBssXGE{%UBI5nApbWwSt zGEt1yo_8+z}lfzj;HfGTZmZIq9aOF4Wb7(^%);9CY3Tvj1jdhUJme4a`&ZXHk{ zC?Y|-PLj=2jBm{OX-d?M{9W7?*sKNQ-0%Q>pkw&(=Dl_`kro`6l zsob6>ul>oK7XMv~ac8_Mm~0<<9A&?X!?Cdkm&=E(7*pZX{_CP7SMu`cL$%@vx0$B~ z-RkDa+SLvZLOVOKS%B)`g=Qn&-FxN2GbdEJ6bfv%^DY}fEexTK3^c(Elcom4n2Ljp z2QkZ`L{2zfQY`VYbSYF`zAPEMcjMxvOPE!jYr81pAel_=6D>ck#I}Uh2J+566gxXv zyVW;r^`=sn@@QP|R3Yi;rdHD^q9dt#B{(KjV;{09xrZt{5erXicQEgGzsPX-O$=HWI4(`aWNUfBQUrB|tYnu__o) zGO#jrfYrVa7a|PJBlrdyca4?#%(Sy8Qno)UCYAt&E^^DK%^?tsvnc3|AEtb>)V{V+ zw#=7#-5!&6##zH7(*_m#iD6I`jFRb_|yu}0o)KHm|+Y*8KP=^7D?erI<*Lx<2@ zVG-{wbt42euxyNjlDZf7Z**eVusnJ}iu@+Nh31;LndW?5@M@6|h>H@crMBC2!MvzR zN>ZQPTOlqPzLW7yUYq8(&i|QGFAUeiRg@?$&&~#9MS67&5gmCIojc?|09huMZ@pe3 z%LvIKI22RyVhVp<9+#n0xRY5{SZ_aK6LKfFu|WuI<(TPX`&X>dhB%Wq@n_`e{2O7E z%H%3vvoBtH;zIskL;XiUrW%?|Tl3Pkf2H&#_LcWEf!8^iS}?;HST>w$yOu{E^hP!! HLdkyvALh3N literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testLoadingDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..941ef2a75c0888bfd76a85502a815835ea60a6ea GIT binary patch literal 6098 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 JyK?)%e*l{}p;`a{ literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testOptionalPasswordDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testOptionalPasswordDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..3b12b3e180bf437008653ac0dea8a3992e44514a GIT binary patch literal 7357 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..1d8bf529e057a0d5bdcf99e078653a7402f7bf1f GIT binary patch literal 4782 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 PjO$nJEX#hr{pjBTtKuF) literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..0f890bebc3c12b5f4c707c3b6fbe9dd48174d62b GIT binary patch literal 4572 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 QrC7#g$Eyz5AAi384<#2B(*OVf literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..8feba86853c0c46ad6e6223aa2b4f01d2072eb7a GIT binary patch literal 5045 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 PjO$nJEX#hr{pjBTtKuF) literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..8705ea84fb5e04af992d31934d322a97d256dc53 GIT binary patch literal 6885 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 E2VL84IsgCw literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRenameFileDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testSslUntrustedCertDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testSslUntrustedCertDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..24b3f52fbb56cb912611031b45b210b09d9138ab GIT binary patch literal 10592 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 b|9s4%nsWmnH)x=Eq7VaJliL{W`%nKH`=@Ws literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testAccountChooserDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testAccountChooserDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testAccountChooserDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testAccountChooserDialog.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testLoadingDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testLoadingDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testLoadingDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testLoadingDialog.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testNewFolderDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testNewFolderDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testNewFolderDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testNewFolderDialog.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFileDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFileDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFileDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFileDialog.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFilesDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFilesDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFilesDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFilesDialog.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFolderDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFolderDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFolderDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFolderDialog.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFoldersDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFoldersDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFoldersDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRemoveFoldersDialog.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRenameFileDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRenameFileDialog.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRenameFileDialog.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.DialogFragmentTest_testRenameFileDialog.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialogDifferentTypes_Screenshot.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialogDifferentTypes_Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..fcec93d81b209418869b49cf32999871821870ac GIT binary patch literal 18283 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 P7LHe3F4x-q^85b)TLb$^ literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_Screenshot.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..fcec93d81b209418869b49cf32999871821870ac GIT binary patch literal 18283 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 P7LHe3F4x-q^85b)TLb$^ literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendFilesDialogTest_showDialog_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SendShareDialogTest_showDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..2526f3183c8aa20bf9c2b085d3cb1b80f1a10355 GIT binary patch literal 17499 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 I+xMUT7v$FUcmMzZ literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_error_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic.png new file mode 100644 index 0000000000000000000000000000000000000000..1ad442cbbbaf81753092f065afbede2c054eba35 GIT binary patch literal 16655 zcmeHvcUY5a)2AL48|7FK0RfK%q)S)ou>jJgO9?@GD4_3vzh#V8Q_ z2C|HI_k++hsP`lKX#UD<;>2KdSy|ZrM99t&CvSnl}*eTNcN@_Mo4#we2hu6}B^H^@s?(Re@x?v%X@4j1G zY?>VywL%W^PgAlDNu((6{=BrFbI_cVLAR)tHJr?N&aamaMs#G+Lifc(pPPhM9*36U z1AjE0HSBrBR#Qe;i^j2_G z4qvDnT6x`uIO&t879(}|lXlp?5j4w^5vtErwB|F*S0RfedKK+#E%B{&#a~l=9=Uq-AH%LQ)AoF|Kk z6{Efj{YEiuC!svP<2jU7WV`Uk$5tIA zp@~C$57iM;6Q9uBCVQV;n!J>eFSC$}hz{6I8qS|wxZzB8|uHX0GPeiImOC{va`Dx5%0D}4_ra_O?X;#eGLv>9peg&!wtXcHYh}k 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SetupEncryptionDialogFragmentIT_showMnemonic_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFile.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFile.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFile.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFile.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFolder.png b/app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFolder.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFolder.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragmentTest_showNotEnoughSpaceDialogForFolder.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatarsWithStatus_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.AvatarIT_showAvatars_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarAndContactsList.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarAndContactsList.png new file mode 100644 index 0000000000000000000000000000000000000000..9343c4fc283bb08656c6b5cacfb5cac94b5f62ef GIT binary patch literal 13016 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 Gcm4;Mr>dp^ literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarList.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showCalendarList.png new file mode 100644 index 0000000000000000000000000000000000000000..7dc05cac41c9484972828f1e800e98bc7a644b87 GIT binary patch literal 9679 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showContactList.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showContactList.png new file mode 100644 index 0000000000000000000000000000000000000000..1531dddecde7ac5092f9b9d4e2cf0146388bca28 GIT binary patch literal 9151 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 R=EoesF;Abv^&UUp{67}Lu>b%7 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showLoading.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.BackupListFragmentIT_showLoading.png new file mode 100644 index 0000000000000000000000000000000000000000..bd17b40c6ee8251394e3a9970cfab9ba8d03de4d GIT binary patch literal 8113 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 TSPc9W4}xwE`}*$2*dPB3iIi&< literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.ContactListFragmentIT_showContactListFragmentLoading_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesError_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivitiesNone_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsActivities_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetailsSharing_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Activities.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Activities.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Activities.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Activities.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Sharing.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Sharing.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Sharing.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showDetails_Sharing.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailDetailsFragment.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailDetailsFragment.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailDetailsFragment.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailDetailsFragment.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailSharingFragment_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png new file mode 100644 index 0000000000000000000000000000000000000000..ac96f0cce34c4741c9248c541a7f20d5a0f0a796 GIT binary patch literal 22749 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�Ul_l#~bwHpr%Ziq`KCm)e&%!Ppsw-s>D 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 yrxe%YaX$gAWm5kD literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png new file mode 100644 index 0000000000000000000000000000000000000000..cc50640110c8ef49e33942e2aef1ad23e6896fd7 GIT binary patch literal 27548 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 Uggo%SOsPxfZO>Kw_WS++1!YyeYybcN literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png new file mode 100644 index 0000000000000000000000000000000000000000..341b02c8800649fc3a3eb0d416b5b2301e0af394 GIT binary patch literal 12088 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_allShareTypes.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_none.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listShares_file_resharing_not_allowed.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_publicLink_optionMenu.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showEmpty.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showEmpty.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showEmpty.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showEmpty.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showGallery.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GalleryFragmentIT_showGallery.png new file mode 100644 index 0000000000000000000000000000000000000000..7a45f94b068ce7c9a8aed2b156c541a155efd826 GIT binary patch literal 50976 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 T`6&4J4EX2+d+WS?$D_UnvLPT8 literal 0 HcmV?d00001 diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GroupfolderListFragmentIT_showGroupfolders.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.GroupfolderListFragmentIT_showGroupfolders.png new file mode 100644 index 0000000000000000000000000000000000000000..939279deac257ab028412cdbf02630ed263e1b8f GIT binary patch literal 5897 zcmeHLc~Fzr-hL?p3Pz=fR53}BuP%U6i!8DPV^vfHid0k(Xu&NkhNxi)B-nT@N`zcL z7X*Yv5fCWB01^a3TnIraTiCY*h(HLNA%TztlKax$>D4ZGI-UE?^^b2Rlg#%^PF7!%H0K`yG|DX0LX#;dkzDDh7v?)+;Bt;h1S7cKe4O1Ji% zuiH905D#}RWT|i}$#Wg}AsIg|4wW`%mmS1O%5@>bM*E*%mcun7`>;oG!u#>u+8d@V z1|E(#&M{}&D_JknBY7#Of4u7{ko)6Cvv!2{KjV(bdM|hos$Ags9ZYeJajt9fwp_o8 z2vke98^KiLtQ^T|kDkmrj#{Y6b|6l)7htm(oZp#2A1x4{!xXq zdfw^@s%2xriKj(1f+?nqcM3`7=(1Fb5LMNYeDAioDmx~OY?RYwY%HC|#~Ye^zgbwF zGpo@LH(`caVV#Tq*zDt-UBMs@UjJ^opR0JNE z$O&r0;gRIAVW|DZvT-FsJug{5I2XS=>dg(8>Abn~n%c|DEQIuOW^W0qh09VeOvO4u z0iV#Oiioto@4N1EcN3z@6S?x{CXWVBwJ5)ET0dgFA18Q5L4*wsouc=N+VUlJ9fL#X zFnbWOb8ope^k5Y)X3DK1V&b(j!xrry+*3Mn3dvfS?P;k=G?qN5h&t{&?V&uQS(Mi^O3JJHrcorwh%{l1&Wvmm8qq&Q; z3|Eb7daOn>?k6SRV+Rf0KX<4<%}jXZn3y0HHqVRG&{Zi4>1fNNDN=F;6cJmtuQr6# zCOWFX$*rY!yUCe&;H)Q7eIC0P)k7W*t0fxpUnWfvjc?WrQE6`Y-Z*vUONQq;Ody*8&t z&>bn>XGvV~%nd5-ex5is%(j+JXG%pP*1-W9UP`c5svS1)Feg$6M4=!j;6WfL! zb`OUZ4uyhrQ@>hT?q5{eA4gA;eM?TxXIFE?$6_b;Rfa-9BxO)pRs*CGRnP1BGk$tJ z(H9|;igkR4(=SHAR9iFQkeD{6h{7j~jb91G#)hm^WVt4DuNiEbrT|T5M1R`sSe^0V zN`K}of)?3iZf-6~kc8+@^}(i^37=sq+M<%nqFl-c>qA5zqb*vPF|rWc3gGNC6^1EbJ*kdHJvic*STAC3uOGdml#?2*+rDdJloOS6>&)zlRvl@6%kWs5g|d zNk%!h&5sU~1$Xi=#kW>E&wsx4*yB(6hlZYgr@6DT(ODfk8dDnDby(rDYsmsQUW1FA zv}WsTJ}Z3S#rzXEVa^0%Y-7Q93Y+MDBBz%*i53~}3ah_3ZHw^!vFi4F28~@`XCF%WUv$%9-)d`KkJAfSM`qzh zp_ohr>#--heFbjZ+?|z7@*2jp`oW}~+uWaa7)tH1a1*kX2D^GD2C37ZdKor5UhsNq zu6@0MZ8XXZ6O4P?|J>T&7)E+MND#6|$5X7%439Nqf-6Bi*jYYs(*u#KFP-zDA5>LL zRk>c)hL}w-9EJLF!h+&<5>Eg(zyv$FHzw;ieFV)BllDeqP6kw$&-pl_ukOW*?}6pZ z*Tsbx=Fox$e4kzox3D7#Ylu(mps?r5;drShq7AB%q=f?aH&bT!AV~|epp?kzxrg3KJ>ZNff;Fb-#w=5G#6iY%<=SFam92`iP(QvVZ-B^qr^9ENoyc}0)Gk*{* z{aCLzAyeG#GQ$pJm_ui5hx_^i(*jNW@qywK^iCQLUmaLZGO|=mX!Q?6e(fIqg5mg6 zDWGBbH=f16vzTEPR$$lk2!$f3e3{knrs|&vMq7Lt+NqfOXbW z-E4l8b||d79bn$s^0-MgNod$@UGL$l39(McvOT1l9bD z#X3xcJ0t2T&dzcE=Rky`hJQ>!O}}}CYRfw^I>;tLCJg|elTA8Io2gVH@9+ae#CT_G z{?Wqq{BK`=KlDlbm+>pIl#>;fPP3gssMbfGi{4`NI~~~Oz4)%8Q@J3YpoC5LQEzH* z$Y1LL&M2 z$W-ePZ1tgBlwWDx2*==YR%o zm|PJ#l|glDGoo|MP*{c{58DAVo?>ux_9Okk}|?Qyw6xHjo#Mj9a?B z^@2({H3OT`yhQETF7E9x6*@$W*HQ|P(Kk%=FbZpK!EeNa7BAL`xoE|b942cidW&WL z5fK(wLu8D$RIQD7Z$LkgMRxGtY1RZ%FPLm*zJL)yoBHUT9fs>=^L5T!c8SQPc@xT7~rRI{Wi zmk0~QDsGs%Y>UZnpPPR>@N)FW6e}!&53W_RWiAjK6EwRFDA*VhWFVIur}xdu19_Zih_AZct6vz57BPZ*fiY2(hcm7cd*98nkNXL;QB zE$bLZ`IuAJ7C0z~vZ#tM^)J;1G2_w1PkFo7M;&%bRZP=DV^gdn@rhGVtAPt(sJ@owd59Pfb{Qxlg0UXM^F#iBB`vDxv2Zq@X z;85Np$qxXt|4$^pK7?Pl-^1{`CH@`*zIU$w$&`O$wq&(hYlq>%-md&F2Ef0#fdhNp K_t18KL;MG42CLZs literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles.png new file mode 100644 index 0000000000000000000000000000000000000000..7e58a2293c4743b60b389797384be3d28809cc21 GIT binary patch literal 14943 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≀A11Y`Z;?oZ|H80KM<=#;=jB1bXt;reK-$sxchPbagpyrTt45#SAU zI?W8W)XxG*-oIlBOji^?9D4Sr`#5;)y_2=aGde=OP`({vK{dB3ZA7Kx7t(yN5m+^E zezSBSvp7u~owNaS2g2gKf^$w=bidkD3kfBFBwFi^d)3q41mB(=E91s|ir5mm&61(P zd*p>*boL=NRQ6wDB@pYbiuAIa<#eSn5S)6;T{r?__ zDnp*}Ksat|7&JNmQ8mS!rdoUcKLSvsBEL!sn%$P=d5bZ3s}G!KL-IRGKM$PC{NoE9Urxx7zlc856aNCAC2y@E4OXTk0nggamT$M$B;?(Kp%W<*| z1>ILdt~S7(x0SQO09PKC87Yj-4el7$Vv~*1Qsh7<$_{q*&b*?t0&7<_w6!WQuUzhF zx593ttBbGmPeU8E#&+q6l2zBQr_K4PkuD8tNA1*Ok#mKdp$O+G<4>U|Oq~g_W zj}CduO;0tN)cF9#e++G~?PPCEetg`_Z2Ev)s$v~=Bh(X_uuGmgR!OGfib-pfjQ_A=Nw5YPn8Yd$hZ6oO8Te(YL z-=<4f!?tbiE~0L}xk3iT#zuQcLCC;6U@WYMJo~wH%z1PNj5RN~<}&{jNJ1MtdE~%4 z-KX<8Vh?x69Z`26)Cf>U_e+4!hfFH>4$SeG?&CZM8HYQ?7P?Th7+w?YJ9gWze#Gi% z?sWXHmDq;HK-V^JOF~VBm~ecjo%ntI4bH-LA?3@vIMtWnVqoG(JkQBYIBv1Fh3kG7T8EADUh!eGqt9fPq5}_<6!(p-ta~bfDq7bMcpNoxF3 z@v@nXw7BuuMYc1$W|k>?bP_xd)-XCPc)MP4cHjn|s(dNO*KsTpIip3PUaB6~5=-n4 z8d%5+q!N%3ChXb4>=RdB(HSCSgZwFz4Z^`uO*R2<4RxYfZfY_h8cuYnpBqB!*{I2Y zEc?{h!bfmAC>JB`KJC=ZP}Q0*9E_vNay8)7LQo0`$aTLB*2hsUYu`Z{aY=SpNj{ri z{V=+py5tv@m6L(QZf?l|W5gc2>0XaEID>ftoelh#!qy!E*0{k1$k1Umr+{wlyPhTe z8H$aIy5WJYUqfYmRh$Iv(SsC0B6*;^6+%|XuThfcWJK5Ao)p}Q1YdQTsY6cOft@zh4|2^D-$v5PWSW0 zV^KC*(#$R06tZG2FnDrM(i1Uu&z3Jv)?Yxc@t;y=Mcp+$ z_Bq#{5$7~Pc;@%;N_r^MoxaLpy0n;L%N*2hzZ|G0$b~g;!6#Kd_@NiLZ=u%j#*0hJ zEHLxKFM2BF3y`c$z{*Stw-!3_y|s`m7$ab&Y~yHQZqno+qd4#)j>WsRH~mS-jnF;* zamPZ}L5)Kz{hOPBW_h=GfK-b;{<(B z>4M^;A@D^F-QV`!!(V(a5*HqVJ%XQ1oU3h{jdrStEQlPOjcSdf3X|>dj-03&uj%q` zsSh2mQ*kD*!8UY_h`lROAl$*CmCW!azOD41ZAIrp?@#tMw;@>gG9TOV^f>5@4lv2q zm3R{fWvXtly>W-glaV&Ptwi#-SK;A`?g;g)`zpxr_P$d*=qy&uC*^jDdp3A>=acNr zKBhQ!suQ7r2`+nglHXNmS`s~wH=TLwn6M=1Hl5!}SunB{tq#xALX!WDl>fg?7VWoh zO%lC7zy|1gvognl7%jv$Y|FKWhlA(>@sfs`QSTp1-?pxaJK|IE+rHNAAa3%CLa1Z; z8ly$;l}R^q3aCHzmZ6<%w3Y&!qOeq%77+pypft#I4hWRyz=1@xMlFnA!hOC1plFvY zS99W{vBUgjTxC|Nrt`A4ew$K?Z3acD5`z0bvtJ+uCXHvO^W3;)^873?{%5j*GnNU) z_t3Qt6<0vTIWK-4#mSQXMyyj6d+6KHJVr1$}MK(Le%RW_J`udd*WBh%w= zng9mdGa9P>IW-h`?j+m_6et$(*FPROLAhsI)UeO@E6Cw)#>@>2wY@)J5@6;^FE`Om#oFdt4CbyXE^m=LwE| z4~Hrhk?%iS`R4`w{}34c{!RZa7hO~N|GGc_?|$zCVod)Kn*DE{gTMbA3B1aG`liH% z|LOn!^(+0i*1^AZ(SOT2_)pLIPjm2hW$(YpHUH1z`B%&Ie-_VoS@?ez&)73e2uf-+bdXZlF literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFiles_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFolderTypes.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showFolderTypes.png new file mode 100644 index 0000000000000000000000000000000000000000..e00c58cc90f4f4175012f3b9e3267df9a2f47de4 GIT binary patch literal 21147 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 PzuHLyW^7jFI!Gi7^0 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showOneFile.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showOneFile.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showOneFile.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showOneFile.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png new file mode 100644 index 0000000000000000000000000000000000000000..c8abed0013a9d17ce3d67286c7941fedbe99872e GIT binary patch literal 27820 zcmb@uWn7it);4N@AfR-E2nYyBBOoc=oi12}sB|wtScD>-(%l^kq+!t@Al==)=gW$vNu#u^ZK@T?BLpKQ%2CF%bj>@gOAk*Z<4$HG=d0`hPhp_nX{b|8K{XA>;e&|Kn(e zES${eSolV_Hbyi0PP#H3%8ChA;9dtg|3rHi^KjQVgL=+bn50;y0eyFre{Qdj)AC5kL>ajwws)@9; z^udA;MO;Ij-C8jJ1tQ({{9xthWi#Ad`e^82L{qOLJK4?D(y@f)epSm*Q7@W)pseI(CxrzNvg9?enURN2DkSiv z-bk2{rX?gMGP&OzCXW;x86~@N=_m4njzWZPsy0B}-5vD_D+>3jni7k?cc@rxc>@C(0@7pqsAov<@o=PdQVoTxZBkv-Py7HUsF}; zr~k)F?=AN~?fNz1udRD1+*{;-+AAL9uQ3W|-&_5E`UEuIf82rUy^BDD^KERJuG~z} ztEkzh_sn{tul35@*1N70OHxFxB|$a5$R!hmE^!TyN}Y1oNPl)R85T@A&hG6TiN2Jc z>8SN3!)#~R;1ct%!Hhg8MqfTzNz19B)zn*Oag?b缾ufRToA?k0hiW!DnDcE& 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace_light_white.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles.png b/app/screenshots/generic/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showSharedFiles.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9e17c4bf22da4e2168ea8840328875e08ac096 GIT binary patch literal 38893 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%0AUKfKz|{wX{H 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 pZ+&- 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewBitmapScreenshotIT_showBitmap.png b/app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewBitmapScreenshotIT_showBitmap.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewBitmapScreenshotIT_showBitmap.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewBitmapScreenshotIT_showBitmap.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage.png b/app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_corruptImage_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage.png b/app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewImageFragmentIT_validImage_dark_blue.png diff --git a/app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png b/app/screenshots/generic/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png new file mode 100644 index 0000000000000000000000000000000000000000..3b286598637a0267c43009a96fe1a1a4845fc783 GIT binary patch literal 31328 zcmce;by$?q_CIPN4Fb}oARrAQAt@yy-3=loT?0ruXnGtKI^jyR+N{*#vsGEckdpy^m_^Ad-v`i-MjZt z6zwtaKSc7fCim{SF-S{@s(zW?YIFsucBP)|o|_H2dantsc`_s(%F8>LM#**tZkU&{(Yswk&cIz2>JIxcZt!Ke%-y|`U)g#}2eeo-; ze>pXAw=~_N&;-w&ZAMOkt<-^#1u!gjccfK6#gxFeeRi zb38jgxz#tqdc$@$hOke)FHUYolgdBRH0LzlGxnPRUIdwgop%iu=_?7JU3+%q6Gu<+ zy+%6B)5#x82RG@^?wdx`bJU6j<#Q9Kbbcp~sUICs9KQRkBka0! z-1Vaj6~T(8&ACLm|tED_ujjJGPP^?Y*Nep9jZqNB5=6ze8 zH%wQVDYP~F?D|!z=M(xxxB@l=bySN&sow9t52nNh6VMmcL0m3nqvF`&ZEd~s2BBX{ zRCDFi*r=V(C7P~&*0P+L{zyWa60h|%jJ(sT>(p1G^F;vVmxo7)<5ZZs5Dn;neH}nJ>l)CsPDPrzH3j5ROgKF zkHbG&l2XVw+MfHOQpi+#7M{-Mr5sF?->9A!em!AutE2A?ZoE1axt(>9jZR9*UDT@g zkdv^7;vJz3%g|q8b^b-@eA}y&W zf~TE zidqbpI{P4e5GWJ%0<2%Je%`q6hI$UWJ!2OU(WUmfzQ8%6Oct^%;SU6%bULjXKL<>r zRcj|@g$lkx<+q8F3YmR)-dB6ecI$k}Zc$9OrqeacuAy3BWZ*H^=Ypj-jl6jm!lz0m z3rupvT{MnzzTG>WbwwYVOtS1!f^0arth3~uFR$fqE($kfNsC@YdtP3H>D9_=z1iQb3q#Z6MZM_b^@tg8$h2L!xic&3taVK2Fv_6Q;b}hS(=h=p{ zufB@s057A}$J>?)VUsKiZX@0|Rju0+8VXD!55f;QlxP)S&;~;bP$;8U?XZMUfFtOA zu~V~n-Lb?QkHy*@Q}2aScI8nW52Hj9JSPFsbWYD2{u&l@N%*-YlPLq_iH$5%+b~Df z@Q^k&kFkW}+51Hs3(*O75)%Fl-~?SnA8p)!u>rPVKh-1vx(7`zXxg{bXB?J0&q{-W zu*gdMDJrM!aZE!|(+JFGEf;vn21;(jUHfQtHQ#(0k>N-a3_TKaRh;mAjUvQ*S;il@;xdV$ zLEUx0Eu<6yXel#a1x^VJ#|lo2122%67G%v(cp>F}m7`L9dGGmYYsRML!q)pT!R2~+ zdA()=lh?-crjPx)pyv@K78T#93+0wg&~qQ$x!dk|`n%IeUxIRJtDFf5{sf5FVIC)d zRinxKSv16b?0a=og^k~ZklQlfWf+d27|yKv?j#NR!|tZIG=qM`Bka+x%YrFRqcjN z(e*qhd3x-_JNLEp7-TCBFK_0jesJg1j`$uyD~}rwjhS^_J1<3WkQ6 zTeI~l<6xaUKD*yv`8zBAj1f%m+@Rr0ITi`Wvu}oNfs7&;0(V~gJ(lCyo*^lhX(;}# zWC&5gyt)X5AN64Q1>RjC5J_T@D4sur!5hqdWrs5uR+Zzm(2Byy#%5a2dw*>(_0ZFx z*{hL4$Zb?DVR_lO2L^Ky?6TIq3meQ0L7M`K{qgJuC1_8b5Z`gv$QTsTV(J~&dVjb0 zshtgFN%hrO&uHsurSUuUz6_V^(J=6&6L8*AoZnx}X-@vtN4V3~;)hBJvnMCMi$=8T zoWe1L6zftS={4WWh{DZ_6*lR8N{4(RScP%AHAO2Pi1h;jb*U{pkZ>JH<}H;h!B!eS zy$e)OQmZ&Qk`X0NR2B`Ch9|r8)Z;SZ8J}5nYGmVBv=%Qtw8f(lrgxLwPiDP#rB_Xxy%N;Pqt_1=;%6lBBa78l>9+JImm0zgJFrpJTHO<}d>T-$B zn2BZmBUwVVb&-Rx- zJUupU05&Y&#tltcOw4y4vn@l^ZyBB`ZrDZ0mxgtVPC%cp|E;}Uf{?vkE8UL+sr4%a z?W;%$YV%l;Mqhg{fyr#0ec|lZ0mNdAiF>xpbbze6s)b#(N!IXTz{CaOsXXSzWVzAU z_5C_iH#dlNgS<)KEj|!y;kWtX3_T1Z1Vu(iXGs{ff5ZELRm`gN4a5IFSjiKs^?~RZ z4b=6}wAp^c=OO9`ef=~A5GQ<*wwmH^(scDs0J zbVkt3N{MI)Ve35~3Ff`c(q?tBZzZPfsP_Kf?tgovU8HUN5V~mwO9Mk1_9^!>-F01b zg2**8GIXx(!^}r=3wFEN>5t7bM+~2}@`omKf*NKs`l#(`^}e*A$FlPprfMT|?VOE} z$ah`kzjoaJHFbA3A5?2o-j}K;yYB5XBILz87moG^4E=IrvM=`W2Wrtvft5ZvO#x9{!a;fX zF_%yMgYi1qf2e!-aU56_je`x_zMM+Rg-3h6zP_GGUiO_MZH4wXj<_&sT?h&`O>iGL z!-z$I!-l1+jT4b$jSmXtUYZ@rQ@9NGJRIQl@a;?JI@J+Q?xFYS>vKJ>G@ZZya2mQOmeQa=dY1*L3>$r^@eSav(Prt zb@?Xvf4#6b^+b+vO{)j0Q|I-8i8m=x5V>>!xMgK&ctjl5FzIqf}FvKrHL6_}wWOT0QD? z3Qe^qI^!Pa!_@woFhDtZZF3x<9&ht{3FFZ|e@EDO(K_UdJ({lgPTc!82zn4v-TK^X zjVXK}IePKEHC$R>Gwv}5G+BN8zvWKyG+5()a-o`U%Qw*Q|7vAi#u{>tQge-d{{j?AA$62p9j zZD#rCHvX|9uq+%C1LFl9on(#G6b-M<>>ecy28&-@{M76Sw48Zct z*C&I4p8mt-$+pQNflhY$b?YS&)3Y^CW-Lw(i=Z9LE02db>8j3aMG?ybooXBMs4zq=4h(@KP6gNdsyip!6(c9!_`Nw8) zp4HwVP7i@^-b8)HQ!ZGRRwG$bhs z=k#j)vj3^s=(9b$Zk?iMv&VUb;MHE6vWCVQ!d4V2lv0+m^49{1J2*l8Jf&c;8mqa$=|uAl_hMWXBa2!6Uvso)cll4_qx?TN z;eS~0|Ah(v(}MqB8AOkn_-NN=><-4r{_z)!z~KLb7-VLs{J}f!!s~zH9Vn^)h{72E zz#spPEcnX*0~h&=PyG4bKOvF4KQPHt(BGj%BSJ4A+V$?rkQh~iu&HFlFBdUK$Zn_>_9#9W5#jp}A1VM_k0N{p zM80u`!-Fcy+Ij@Bb#tzl)>`Qm@=c9e3>s`2z57`~euL8bQ2vsDH&3}OM^@BVax#|2 z4mJ96@G4h&XVsJlj&z7+R-n`5%0iZjsyL}s-o86MZPmv!$!c)RLiWBWI+pfVQ2efB8!I7xN^4M&Z?}cL>XHLf5pzmXDyhAATO11UGGr8d=}?K zxe><>8j^WLctxj}Weh=oI8nC*7FYpw*qk-2=qAs6~@#e&F7Tdu{`i%sC zl`Vs<)5h}J?Qg&4nC+S2m9PneXf|rE-vViINi+b~p%{Saf%i6nHJPV(CRL!XkG=7Q zRwA~E^Ri+%X!oSR7VYXCe8?;Jr+wkJE|_NYd6i5m0p$U)O< zk2w6b@9dpD1JMCZc4|wjG97D1u`gqtNa<@E_sBE|1SbRxHzdj=}Dum=fn^Mjl;59eQuRB*irGzGn*R-`kX{ z(^F$rQz6LN@_Vj)vGcb6wN)+*7IR9z4$b~K^U{`2fxnQR)nO$s=-CDi1v>r_q|H@% z&M`EmQAAVk@S;X2uECQ{Et**7xOm0E{MXZFKDqTr1&oXitE19gm);k~O~ndwVejFz z=2JjktO|dg7VYB79KD}-e$W*F^}7nnrar}x7~r1S^YINQ_SbT60DtnpffDFr9tQNy zm2@BiOb$-7hIKopTs!Vd_p++b^R5}h7|6ryVwC64nyI;74Z;v*ZceE#|7V6jWKGMac zDO{ZdpPbn)yLAfN>f=%!qYZvG>eF&GQK}eanfX01v%%_MvcP|0Sj| z$1h-Ec51lY6F+t3I}|uL3@_rS-L%vqB^Zu-B-Gr`tj-_=KD-rj5RdfMUpFy2d%G{> zkg0rQx7P8yL(+X2e*Qghr=ybaz_)9+r(R%X|BV(?Lo#3UF5V4wDB^_(S*_OolK+4q z0xR?fE;YY|o9DTtrmt619o+TvFHty*$V~@oLPtDmyB#F$y7s-HH53-F?=hq{EhBIm zoh-T55L>-Qz^ay>o>v{NPOn}2i|K@3lzo?9x0;Fjv#SoQ+4ZF1)Xj|jy?NJM$u)c+ zoQSXe>Liv~bRB-a*JqF?%*1A!vZj_+>?(EIXkAZopkHZG**g+@Zn?b5cv zxBFLdi;Czkf@Pm=Is4j>!JH48y|n`^!gQcB_aRdn)cfii41(+!HYnHbv?GG-k7L6i z&z}t4Ue3%1(jjxpoft$Xy7tToK~bIvsygT2=S#_6s)MLTWboMc>sD|k24GU|XUnDMFXQek`oO{0#x5p0E3znK}j@6PI zz*Diwqjfa~@z54_e9IBxQ#6@AStI0L+ZzgT27g~FWjN=%@E&`TcwxL}G0wi8 zGb)@yT;6#}vyZ*2TGNx_=>p;o9bjs`x4a&WTdXGrI6N)}J{zy3NMwsIn+8ZOkf8(z zE?GQh)jLDewzZ)2D>%VPI=$5oMyNcbmzus^=z28zpnpU`dRO0|x5Kdo+(_6LHTp(# zk?2F&{wMfm2rQPbjoPs3tT-=5UyxcEE)`C~c6zl)Tv$Z8w6@!H7{1lpi+)twEVle|A{onGTxhiIGGJ*W<6+sTmLw1e%c_yDNBcJ zsqu5z<8VKLb4eOxEi;pvMorRF-Y-e>eDz6B*YH5xJ_Abz`yF4JIj!>=j6P|v&s>k~ zA5dlGIJ<;1ixg$h>Jb}kPnG#w8O1?}de^l-yxR-;>X#7&kwQJ$jV`7fgtZJ7Q*KZU zI{Z~;2y>BZTapj4{#j#+;Lx*dLKC_TN!n!+h?K`h1TRY?R82WoJ4CJ?)F+)F9?XND zUZghXgybzjv4#5^0h_YW31wjn-UQ$uOJ6E5_k256Ck$-4$i-FDd+F`Pe#CNGdvO^? zYg8YDuyV$5yNZhTRD4y7S~~lye0IwZufw&ZE>fTa56G$HDtx+|8bAChe@AIK7lcJ+ z3>6msypEXDrTeWZg;NGit}aTxi@5&1HUxxmjiedX)sw%Q+;@+`)ok^4gSIE$4SQ3I zx6j=bJPD43VyN#>FVTnxRW(H9Z-4QYD1=#xQkN}CITZ8JA>Ch!+`(SHR4AXVS)Ctw z9`_Pj*zSKJJW|@Eid-BP5}g-6#{&vsPmf;0$%?^^Q<;q~Sfyi5w&x-vpk7a_-h`|0 z4|RlDoBjyDe|>p=EcUVOM^7|h|BJ+cRKYTx!~svx1p9}3>IjDiItoN`Qk^T z6yn=6Dd_}`xmfC6rrk-r9-%MK-owjfsChPAk7G4dsmfCnE`^TZrN*CHA0@u=OqzzH z&Lgr%+Bbe|`+nY7a#I@H_P!=g-5QI5ccM!r=GU zG7u5jO%OcY;__OkINd$;6h1*_l?;i1 zno!ITS<#;}J9<#lGLzu1qVY?|$Dagw;o0GXprB7WTC7MChcXQQrcGj8sGX#l{F_|V z$0G2Khy}lbXoh;g8HDx(7pb$HG82O;v#db;4^i~ujXt^teqgx6L3QxJEz?P9in%&1 zB)Bfo^8I*ly&aCX50^Nmar;i;+a=K1YI{;dtdQr%Q#DhLW^?OnBlZj1T}>vx1Z@L+ zSubt)K4m51VZiw)dMP&_mPQ>RNQK`h?5bg@;$Tqd#os-+Gmdpm2auUj3ZI>CdUS?C z$x98rDUvR!(yvY_R~BP*+$~t^Uq$F+wSpL$$b@AngS;&cw9}~%aA>)ZbMLfIcBi&` zVNA&P6W6EnyN5FkoN`WySuDKS^r($!eCQCy)bh^o{%t%hS=v7{a=yX`F~e*(NKim<(C%4LK$*oq>t5fZnfOy`$R&g zUPScI;Vs7}N3)X7)I4_bx!o!68ujM^Wl{?`bUSIM6jvqIL|5bUjm)6;lZcc}c#G^e zLR~IQ4`^|$NJGGC+&<@R;ntd@AN5LYD)f0x4kW6FFk9Pf;lJ(+H*abbjp4Z9idXq)FOIidE#1OUn=&h7dvY8Gg2Xd5!;+$9I_TZ@8$hyK;$Te5xjvEfoL&Mfo zM07L*e2%nTEMDuek5+D~vi;DGs}g3vzBt(jzZ=T95m+ujrFA9xNA{ES^n++#1}~-v z2~^sdC?b)@@!V2Fi<+ceDPEL;%T@6OG<0mt+@?&czhkxIxhKQxJ62^bOt)EuU{&zal6pU0a>P29kr-v_%t}&Z+{7f}%0% zW9ki2D>A~J!zdteVxX3^%2a_TqkcP^$NCWW^ZUkcX)DwdnelMSPK^7T)8z^t#cBx& zauFsDZdt@0+GbPG0*-yk*zerlHpLTD9oBmB;c;5E3L@y!dP~`br8_=o9Cv?bGA=d41)pckt^7Yvr^uc-;YJ zd|Prn8uRpxkx@(E#k&v@me7pJ>o*UWd3h0W&Rz-V5n)zv!tX?(GZ;CJdEtdl87}s1 zw!DOXC*rlg8{QZceV4vWES^YQtWoDVlUT79n(x6+Mpw};Hh{C+i zOT~n{fZ>K1lX0+ek19^T@}ZDTeXj8#q}9fL|J+Y+S?&FCmxH+ zLUgkbKbG=a`rq%G-2X;UXIoq2V&`cSmz8c~$YciCwXxv$IncpT)Zd-eZEBK@; zBqf!Ohak7=QH}D$(x6`4%K@XVhR*=3E$GjT>w2Gh+Rv z#neTrBph(IG!}j`B69^LM#W&7Oa;-xkz2=HD;8Qxlj}=|Qw}f&`Zn&G0GrdTK(|0y zSKh{ZpBkt>*-ZR=C-m)tBc=Qhs5LOMvHWIn!EoEEg{OADTtKZX$n6Dl64DE|;WglF z&oH)eorS)@KJ#n4_Z{K7pjlcOPWs6&_3aP)iuTpGUx0l^HaJ%=@x8zrMWQPVJprju zk#Ps6MN`Fcf`rotPCY(3OhLGd#KJRY^k4&(VTfR1%&fgN`9Rc2As0Pw-~<5@xI!WFgbY0H3Pu-lX4 z=UsqzX6>|w`Ca#4r5^mo+HJ4B%(aQmTQ!k3{Q5-zoxj+~WV;)*_47djmNB7UaX*%t z`6u^@0dw-vFY##|o5ne;F(|wZ)=Obj(SeGYNIryWLjKbyc}_h*0pr6awZ~fc_J^ag zsy^dJoW96N7k??@pH3C{5J}GQ?@qOCR^teMtxsGlFyN#Owvme=J?ZCF`t7`x|+TxFudD#~ghIf>zB|uX_)UCDi^eAD$ zBG!fSBFs`l3*fo%pl^tb`sL%cVgHT)7?I`T@+YD_vuQ0ApC5kw6i2{9J*=KiJnCJa z=Ef0Kw)P#fUWMYnf^HJlk27mSLe*2`)ytBYQV0?}i(I_KW)pJy+~=IjDuuhvfIu*G z?6sJvZDoJDfE1WB_40VDtnCxRu8QerZXFet1=2*(On#NOfK(A*hfj-onTwkGb*BYc zW=5^nbgjF%7Hg~e_vxeLh4}{QmDtk;>)*znkuZ;|Ck20I ziT;g!6CD99+!cbloX&7OWK97=y3w1F$=|Jz9NN$T&~<*uMol|Uf;#y)J+tOr%&!^~ zo4msk0;z(CBKEbP=xCasz-1UIJ={hj^MnVmW4rrw@}2f7PKd zNp0DOVaw0@x8Z!8BqD(7XKoUlDDM|#nysv zPpE4m+g+->woYE)^2#UZwcpI1g)3O5OKeBkbwm1BxHB%2?i z3)$ts`E<+`Af9MwsZ=c;utoqD+zyJvJb}C;yT{b}t09%04Qume-C7u&4rcTYGtU(G zV-_MXi8}QW0HGZ4{ctZf`IrQ93|jdrl6B)JRpju{+iO?vxYKr<-pVQknbxk47`Ktu ztIu5FH#=c@lajoxQJ=Y~NPbgiSMxI8!SU6MIY!XZqze>)0SXuW&}FuRgo@*LK2NygaivQX|gZ@fz)9eGuRM z(StQbWOGpx)A*#Hv|=yW)$q0_wIm1wYpDx!6^2a0g?Hp;Q+?E9`B$O=l>l~RZdOocqc`*Z)#VF%lv;LLPw8yb1^mCd^qp{?R<9eRJ z%mhJuJ9270IscN<2A~FFI3;4Nr zSj*tWv-I;bH+q`tq<1foF9us=XYAsiNs<@euTd{c7z-Vjvt(TQ(rchoCOnyxe%`4m zNBb0n-}R9K#;;SK^GoxmlQLo4>7&7|FtbHgZ}8N`M@#QjawG0AAk0niEfFl9kaRH* zu{Eu*EiLRXizAMXEh8tT$E!{n(AIXnIqN@6%=j11|Bn+0;3O$lJXKQ_xR{t2>qSHl z=VbFcGxW>~s=bVDWcVO`sjoa;pROyf*$uL=uzl}3vEWej^Ht$Kzt(bViGD(A$cl)j z*U0qE9({?HoeDv$7@?I2(Aa zx<9_LzsfgA(NqtxekZ;{&NX<Yx-n!Ne# zWg@?HC1=Dtkuue?UF;eM5ZQW)+E?oRT?Bt-R|^fXwp)=#$>%6bo`C9-_luwkzE%3& zg>USpZ;i~On;r{4G4G*=LHtC9tCF}#kwB#&@S(U81IAFbc2VcC|~e= zywq*jyC}P#JZNnsso9c|RN0FG*N2_1wx}@2!r~|I?k-M@Oq(f0J|tR_&FUv!ong@9 z8KqCjGN9;B5jK{6lg8z}#zK9@u^6o8) zHdZN46PC+;Fp+5Tv^Xa1O&jc{jQ%HsHuN>guXUTX!TMCih|3>;^!y0XL#8zyh>d|k zjv@pA=gzI6M%TQqU1RE$Z;B)ka&@=dujIn@j+`YY#>aaLUb*OLK8Z{|*Ux%%)^2Nn zB6LGupSFNl$Uph5#JKy0|7AlepQk(cU5Q7d*oSG)gyHNJ94hyUdBe#kR#vA%N=nQ=FbtOd6ZI|v1@;OoLX#2}370kk-F>cfb#|+lLKQ4; zLS6S7K7YibK3BJoi-rDC5?ZCo>Gn56ag&t;lMMmkhh0LcEb56&7SU>jKzSh6TzV@w z45pnNrre4M7%;Z)k2!tcL9CO$VaWnyd>4pGzWX&I9hd{FuTK{P4ldosWyf0I)o0BJ zrEr4I+Pc3VqtFchc>zcuUiDR#@7{?rfW_bub9km_3$@91OD@ZO&0n-HJ$a~p%sTEc z=o-G79Qf&|FM8_n(+PQaST#PR%;squ*$RN-_6OB0%uFi>-K8tTzZkJuhx*i%-xxzq zBeu@l+h5OX?iCIw-wbiozw!>72U1k79FruN+tIezgK%6+86VRDtG;#SWD)-fg6 z3hFVr+Tuu^0=X5>>FA*$_Ma?R@weu4q6>xn+UEMBf)hpcf{%NP3qju7W@kS+eQhfE zOncq47ej(SLr#A;y38vzXFW&$SVl}Uzw$snSwCJOHq=9nMzi;ZE8AM#$eL`^8&th} zr(K<~X|#aGpI=g+3z?(7b_)XO#V^@dwif#-pQw8mPzcH~UCJI+-HYHxlkO5$_MUF2 z_mzep%ZN!s*{_*1?GIPR1C*O>9MgXr)YAa3f_mFMpkx9e5Eth^J|9=$UB`-bQEBRf zMyMl}$9J50+3CW4sc2Z`SwRWkb!$yytx2pb0iq_N#^$t*M{P8-SlN0$j?Mea0?P4} zrV^&f-JW)$u>?N)X#6OB=;?On79Or+`t50LGxjCF3WI|3J1Mec5fv?H4>9=15B2BB zTgUdtJ%TFugrf5*s`T@74I}vc28{F6v8#obg$-*Y0QwD|xjj2q7NV7IYLiHWXwMKa zb|N-$*3Kg^sd`oVDmmO6Os5`m3-3THb&uEn^hy3e5DILdBta)G|FQug;}&& z(6mI?BPVFXLP~>;-U-n?yPF*9)Ru)Q=v)*-<~A>LQvu%}w@ zdgANN7xB{2Hy+=v&t6uW*%R6fQFo0PQick2-JP7T?+~xd0~>(0Txq1+Bn*oZfyq2P z2qJ#zFL}Xysc?c``xg#p9CRE2N}|Amcu#;!4_?AN8r~y)TiojBFIoGLE~* z<8nWm@;oXjC=Fpd-qoILRkqAz)m)|l6GU@Ys1>P0(vB4XNEsyS=2p}^Ke>Hu?QUgO zDRr#Sz@<~dqT7ec^S#P6MAsTm_JiY3?v?2AZUDFYKDslY_8=VH;`i{kshZ7-tdj>m zHrKGO!l@mPj{w5=KX?sBQU4p|k)!7TAp(Xm%ur&{h#-gH&PHpnZ%JIf zn?KZT{8a%3zt9%n7T{$*{7g!bWd=Pxs=<=DvQ5(Zn5+Ze21ZfMiP)W_B4XvoaNEQ` z>YSZXXGPXF=m=z0Q(=p`R76dd;iRi0Um~p|*NC^2lh-G;O1YdOZnxp8iM7qY#w#eD z_uA-%Z}|*Et}3P8$1gwS)`ru{h4k>Jqcen9a6|{E&X(2p14t`7Q17tl;4tb?l1;dhy2)Ba?_ zlhyjDFI zS`k!D-w{32NhL?6>UzqC<}0aW96^p zb*tbE>Y2Teg5C~LBLkJU?)Gkpb_{PxKo$Sm$=tgrmv7tt`%9|)RcDqW(N4lDR5SzW zJqJH~FtUj(S*in8#A|uzKV}!0j?vq?f@hz;K+5xw6tjP@MqmJh9$%`sZ~R&{0$*yx z^fl{r(o&!*RO!)#aX(VtqX^+MyVIwz6dE==Z9&7f#qBAa>kL_CG=9@tTDmPh)i&pw zw+V&G*D0cJR%N`h*&yP#$}z$sVP`LkrAk1}y9)Tg;H7h#kE=E=r^c4MxzKShZVf0w zOpJfh&X=~AKjn#PFp4-`^c|_0;Q9~UZ%EslP~#|l!g;7{^GGkzucLIWm*w0|fu5a*fs^v(^cCV?Y>gsuD*)mD8$bENb5xh$WSYM;=Tw{D*M^(;^ot&dwo&6T?(t)DSOyI#FSREH9O&m{tV z>nI@5FY-X@yD%d&hu34gqmLn-F(E3ah3jre>+_TF)JJHh8IuAXqy-tt!>_z%HOKA> zB{J-3r<)Eg6@z8~l6;Oyhm==;MhjHKk1W+)g9bk;GVZy_tK)k%%2tU<-J$SkCND9a z=cjj~e(oKzbhCVU)=fwiuNnHQ21um94$Uwbkqo-@LDD{q3Bbg-|A z`dk(lv}w3V3n{}py%B;YtHR~XTfSVi`=eVtAiv@~AK-_z{MG9!drXFamJe|BJRy2I zG=Nj|V?|G@yLxonVZR!7)tX%V;(>N%)*YuSIC8b>fq6yL`#Y@z6f(SFeD!Ck#V>~M zZ25`U`$uq$7ysgn{iD<(+779%R;>Jn&|LmbpD++IRqxbwkVRaI*{7m}z4jJJFAwxQ z!R$Lx6C$>1FbM2{=30}3Ib5~6>r1o&Z|`Y7V_HjO+|c6J!h&E6cdg}|Do2s}*B6yV%QZtOo|jcHXz-ax65Vslp6c`FB*oV^P~mJr^}!p(2Dmk`sB^jLEa8ma&)OU4O7M3=|wRnpgSGPEpN9kW6>TommM zJDZo;h4_rV)|7Ehy}rF()UxBez6(4^EG*y z_@3Kb^-Y{IdU;2Uq7I&y!Ws)34u_*;s{lDm&Bfb1uc`33U|8K%+9{bvwu5*nrh~2d zJNd*cL@I~W6(g_W_GE*wAg#8yRA*c}+z0Rv2WAIc2wI~=(%z0EtT^x2vZU|#W2b<0 z=SntiNY=y?TCB^2s-UtSq(o?G8@Yb_5mL)L>{76%A8?r6W`P)3Hp2(0XwhCzc6aVH zf9j>)KpDl&XHYcAn7czbbW${HYsM2i;o@5H6_~XwjMBrkRzDz{E#7eEkSH|`Fn+_B z&T?<76SlXV!ivKi!%PqNB}nNjw7lbwYe5(!M(%E*FpB}EVLmyc3Pt;|;;F@f`GeQT zGur5zf@9PI1)Z7Ti6P4$0Mi9Ti9>ie;IS*znsYHw0>%A;3}`u<2{DfCbdaN?2kX&BA9i#iM?LydFIknFN37hM*K-D~0OqD|xU3I7 zg6?U7wIs>%dc@)Y{ya3DU5OQB*%1`?yi=X|QZBByDcbju!kUmGmfV7(De_`m@Av1P z^D9BHa#d^Ogo^q5tvpbrhu{6R}qX-NDO_%k`l;~vqBU}bkbmiQ(z2WcdDkIwo zFZ#aE@+NxI1rh1b31_)@Z&61YJA`+-@Ayow3A>v07vO#Hl z0|rYnAq!E+S|KrvJHI$VvP9x08@#Jm`)KnoUR_OPu_8Cf5K=|zCDoP<A4Txdaum!#;)FD80Rij35XXhAE7l`OR9V=J1 zBtg?S?tgJv=37D#|nbTzu0il$D8>YXhJU)DhVrrIU_CEQ)lhu! z-f30jki{{$Z@>kcz9+EGR&v$+>%I7(tA7gEl+kj+@ zbjiTIzwGT8n%_UVc9zT`lUIuFe5`Ot&cJ&O)T@Y00#~B>!h0;Lp^m!e1y0Dc1Lf$M zxs6@7)-3xky^$h!Z0X2^%r~sYq!W$QA_&Ey_?QABPn~AoA8|4-+SvUCI)Hn|!A9Dx zVsYDU778-G{Da@=tbHn{W@vuIVWOl0$%(nlCp>YY3X9WKrc5zj-d{Ur<0ZdqK4h-| z)SrLSssF{U#+QO?YkYGEHnP+UqvK`Jc~xVL%^z+Q=p&{ynX;-&;L+ua2mtw{5G*sOaNe>z zn*WhlFt~90T@k+f)w4c2?!|{oXWQ;BJrRsoH9iAUWdm#ErOotu!dJgs8Y+raFHY83 z*_)4|u#odG*bVnwF@wSf#pH;Fqs5R{c5iP3!!QKbu&G`vCQh~=W5}mkA>VOn9=tjS zj+o#v+8LAudJ3M;i3uE{rt;~zJccbU+d+ttGQ+;2(uSYC0$fH#25)Sqs+8t-X0>9? z)lCD7?4)p3@>wBit2EwQ}h9 zS)N2hUTbN1vGm$5ji?CV1&!Jm0jF_K-!idziroEP=6}|6Hcl5a`)K|}4Eo5 z!|T*H>y(up4EnmWF(K=7f!b+=1$69rCc^((r6vRvY=FQ+y8>>4=VTCIK&q7|Xo6r2 z>0fBNZ$0eGP^hf7&9Ti8BbVoE>Cu1U^ETJdj9Jy_&)zqwm$yX4_plH1CI1WvKVSkl zUb`xnqIQ?3o6PB_ThH&Rbcb!Hl>u|=dRjdw6AOyt7Xc{gNIZL$JB7_!7vaS<$kKeU z)B}%bYh>H|24E8R#`eWiq)VPHP<+SE%+O(SVnvhpwJ~d7O3X_?K)(p6l=Siq-_=gp zYHi66QgM@wYe=~wBQR5`f-C8dD{KetH~_JHA_@kCZOyKA;cVCYwt?*C>m+sW8ze+` zmgF<~G&UHRRSX@Hr1{QLuY{L17!|PP;1M}Y$~8qRG!%q#K^9?=P9?xeQdy56{?iBC-EY|@Y2+0v~VdcvxTP(3d+8UeHl*{4X-|;T*^nrb&@b^f3;z{?ju|&BquRy{7k7mpM94nMz?UJVS3AUSA?T_b9*H4 z4RmiXe5JtIwk>x55_)T_=r|1b@` zuEFxD#@)X~Mce3yvRQQwepa&q9JE=+@IQa|;-7az%$+wjEmn4<*Oiq$$Hu+K(?$+( zjd*%!D_#5vOQTm97yAo;*mh^i9csVEHZNd2P>RR34yu*)%HUM*7$hRhH_=OY(P`!b zP_)b+cyTyKZ?U|LwY7!d8dV5NrbwslrBT3SzXw+(tQPT|b9WY!)JO^$=(ewYz3YJS zY|)$RrPg<*!nWiqqSk|_6{z*@5s-$|o zoeI2d02zrw%{6=3oGvFD?@kZh6?OsUvvf7B8Qi9oe6#M=MT5wjH^zfiaVi?2AbJWD z$VKxS?Ev|+f6SVE4wxqt#YC7JSZeoiMaS~#vQf-aRuk1kOE%;_1u+zTlAkSZEU6KZ zOqe;Yz7n~+6ju(U`EhxlSz-N6W9Z7x^t?>0pg&(fvIBn??Is`~$f+y)dMgRc@FGnc zSrs>NW%S$UXV~r&SC6jiZ_b!Px<#K7uuzqPl&Ef~?>3bLAH>k7QWa;ms@bW`$lh)3 zAljw-(0Y-`kqmHu^6DLYfp?dEU*pvZwI2RAjs-x2{=1}sifR)%He|xXdrDpY^>+-5 zNBp~mf&8(k3H2T4WeKMpWJwP_#$`deb`*Wag9kKUjr?DE#)4r&vI-FEs zOM)f0SQM1tI=o@!OwRveukJs%O9ub*x;XG%rXSjNAx|0lY&A^-4Zk)RN&R$Y-SQaU zAHeaN8R0SEW4OBzG(Twh?Ggax5gUQFUZ;~r2SCaFJwQL^-;sPG_tP^XX_0hv#rGQj z;&_{uj(D&G(U|(m_2+a0x@$|PlHX$-H0CP+eE`5w{Yaa1ui?d0p=tBGSArHF)8@yg ziYp%P9SYJ{Un2Xj-JB=)=}Yw}$G!_qPqZDpX}mv}7=3;K7jL$RJ>=XQt^j!7uQtgd z&Oi5aeP$y51`jn?8FPTu@T@&%fA~do*j=pZ1#2u3V3$NR)xU`t>HBOs@crT_LtFeW zgZ%8{R#Scx?;MwP-Z4`)+%Hz;m`cG;zH~gN=L2oHT_(b0hggA3#O)#DZaVm&m<8Bw zlK?&ae2uX-V01m4T6O~etF|u>hqC?qZdVktq_P!7_9RQT$(FJv*|#J~b~E;^lq4b9 zN_HXpzB5Xg5M#-1h_Q@)8~Y5;c@5q7{d<1T`yR*p9PjTh$92tloy+&!KHu%bXYN3F!0a%kYDC>E+vc6xGn-v+Pxka63J9;XXE<41w1z#L7x>dw)HjN! zV|mL=dn$5Roirj;{nzHkz`pkM2756h<%!PeZ{!9FdI4L(>efF;Ibx>V^KM&UBpc3i z9(lOZaNX|QrChB~zn|13`@_4A3HAoE_`d(xZqKTs-LEnN^iGWd%o<4C0#5cQ$s+01 z$C7Girt!jaS+nKov$-bOk#2CL_EKfveN;FsEPjgRW9>&sw~OYbjV5GG$=yh<>`~Q2 zy;qP6OZe1uehT$U&3Ts|XbbZ0!=Kpr9<2OiAqUIk+^loFhuM_zxk8LV;z;4jo5M1u zuRt1%Hk)vdp-Ho)iJLB)oDLr{SFgCk;^WB=yk+H>iyyPFu!5?}7Cyhz-8qeD&@k6nR>k_!Dt7NWEyR zD85lP7MU6wAq8;x(w#d0$O;1)QN2X?GKE&Ygr87E;MBS6dK@Rm{TY~xzFwSozKun_ zQ22hkxu32zyr$u}yYgp0qxnk{r0kCHci8QKdmb_&j|MS0ev~6}=1-+w@eA_3<{kuHC0HyBbHX2^-+wRf1Nb0E-zMhh_Ee>C_EQiDQnO23$>2Ht z(C12;DXQ%5nQ69jJjJh`+kbly+=Xt8HoTG_QkRv*6+_ajR3lHwZGLRJ#=?94mVP%7 zHbMASn*kdihnF<}!mL~U)U|K=!VdY^9dNZaon9&W}g5Qsg&b$&F zx94uD4?Oc@_M_0F5z&=eNM({KGL3osjo>EZ8{BXrZGt zo2jt6K^^8-ad%T~AWz1Wn+L&cmUR}}wP91h(KF~ff1f`wPA1H}_O5s6ad%$5>(=C_ zg~xNtk5&s>nnpsRaG%Coo&GFVG`n}c8Ts`YO6Is~k`{|gE zJR)}Ls%=(iqSfibe2tHK7PS@m56`$to#NI|xn0lzoOk=`P63lQdk%ykBGS{}U-Oeo zL7)Y^6YJ>4U!72|EV$-nwgC29(nZ_>p)Czc(Wg0L`eBe#iDB1_At`mF`DO@6fVNnc zoTA&sY#hd3Vx&D>5p1%;U+mQj?P@>)d1ufXIjm2ME3lptF8vadEkOUFF$8EZK09oD zQMc#XX~F%KeO{v3F8e5g&%i>ll#I|je4*Gg{x(Ng-UaiewToe%*f2z|== zfy|fWn86Q&ei(F?(Knlj+s%HyYJc*(|4JfNrR06Z+|(F+WwtNrQ1MHvu8as?Wr$V_ z7;I>CPkw(p_Z1UA_>Po5Iq`u5^5k&g1iuCUR56#~9ki!7!)sryE&_3HxrdQgG&WJc zCR3ITZAkx@XjYN~D48F}!6VlX>*hNF^p4f>BbZ5ByV;ihnfJiY=T~lI((F&Vqw+6; zyYp3%PnH4Ao9k+)c>TZm>WZ071_T7$Gt~V`wpWH7Dx<>4U-$lUVnbcJ0WR;tnbwy2 zM`EwV*N79}p+pS)2WkW-j~s_g$YoNGo{wMUg<;Qv`15yVXa41vzK_Lndem567hpei z;KSLtQJH~}iz9TYDMV)rK3~cKsXgFoJt}qPS$!Uq7vd+SWB4O>l*GoeuND45W&3Po z{a06=iXGQKBe>F$PiJ6OXv!*dB<0Jha z+eTGd%WrQUtsDa`)}{pu+RpK~Mgt?%r|9=d&a)Jf`x&Ke&cerd58CeQj5ge#PvH+( zJHHHiNu49N?Yyj=rX~#wOSwr44)9dO!@^W5n5&7C1bdZ%j0wq&MNknNXZ|E$8l_-; z)@hfxt_*HS_lvb|iB0{{7P{hf)QUw-ev#CGAIw)R`a|W(?8Qp9Cy8bPUNg2YY$J6> zdy|f?bgfHO7I==*a>q2@d+;UlOXIb~&eaPs-E$EJ1%C74>u8U)yHk6fsM31nar0&1 zin`dWKY30F@q(^@8G7bztt>?uF z6K2X+@ARQ+U3z7|8om&K?W!>Ejuyr>QTm>-(A1l`<6u{{6D>ZRW6oq*-bTX)?XNqG zmWeZFYG!%PBPx-H0~jA3&MK(suR5dBE6vM+|a z!!hx)yeu%Whl$Z5cm)d> z@*H0!d)(R1hJ{&)TY=HCZJ6-CCih^eU3a6po_&K{t`PaQWDWkJm$xC`GL6O8e&%kb?^{Nayl6I6s{K_~$91EREMgVV zOdQ-VNW!fDJ^%go3>HeLYT-a-ExBTvrHr$NmOfqpW}4e!LDh0?{f^_zR@w_2DqzoE zyp+_$$oS=f-BO9J8!D+wLYfxh*AOx*us@b}u5ry~>M83IDHqxn_{mNwa;Pi4>oM65 zgDfZBx3ukte#;zA0m7*hGP3`1lMAAK_;$VY5;>>yC=LneRRj-rc(2 zPhI`Bi7_G%=?&nM+kBq|q1JRpi@4dF98#fV0T;jo3TL`_a?Hk`Alb*xFd)MGgf`+q z;3>I9!Iw3{3i^)2H_L&U5y_&|Ht|DG@;CKMS(a|jfY?rW$|9a?H!}B|y3UZlBV~s6 z$KvG%?abac^b^Ve)Up(%WaP{q=JC=L2PO{|r8cw1Y3BHV#kdoYO{?z+=F!Mc%R+`Q z1!2#gLys78a6h?F_^-doD&Una+t+{0mxjgXcnU^VJ*{`FQ95gs@uX`S(LWbzOGJEX zov=+ry@zHgOB9HvZgK1)X7ogn{ltB@PrMmHtfJaZ`P1`SHoWpK2DKt$-1;Aei%Reh z^5oGSx9TnFkARx1R+qECDsB7YC?{vZy7f%>Ec;2^|-QWYVbr z@+JBR5Yh9*^U>kJ!c^PzX%SknV1Z^;ornj5`$Nz$bOc;NQ12+M_tI8re45f~v6vAUj0VNIr0kBBRm9b6@20qMn`uE~~0$E5XwvZ2cKT9(suz zpkrrG*2zu63)QH~*aR1Sq>1#MRxMx8LS$<$Jyrg&iD3?ss@L=S?KUS*_&WV&|NZ?$z*yM+~~kdwO1!(x*)YS0-+*^7D=%8{$ss+ zG=DYX2j#{7-RpfA1bPjX4z>h1h;%vA>BHEe@r+d1gM|%FehT|Z5vWX<_JQnp602Jg z%@g8VNCKBy6IX@yz6aTsP(4r{Foe8f<&W;y696yMWcm|N1m==NjPgfxGLs)jP*mLV z-hP#Ih#U&24CIeKG*}))5;Xp=rhSK$684(ZY{N3`H*qx94B}RS#ztx%w4(968@-42 zLjJV>hks=sN$>{{l*T2&-^>+*;##6UB0!hcXZVUPMt-4`yX6qsUUGcM2Lv zIlpAb4qQY3`3fbyh`t|<$HJkcixH1IB2IaRl45s0{l1;n&w)j^_FBEZ;RNe z055~vUaQV0dbax36;smBto_-DJTj~`>?S*a$OPZ#D(`q$|F`WZ;=F`ne;@^gs{pC5 z&H;7cD@ndNxk(=O^K!ZW47sG$j!6yh`f=Euac^L)f`}-@-bMrVMR()l(=(TH@dZ3k zP-ANCZ;3j_>}C7WF$kbT_*NrkJotq5T;zNhYw$o8N11A$(>>-IuR^uBk2RX$tV=vX zM5YgnkwX6zdq>uN8m#YQrl?fV@5am~Wg8mx=J%$(GND)(CdUF<$H0`@yzhQQJn8mZ z%>nuqS{Y!O{);&kU|rUAj7RSM$cT&uV9#n)_t^?7`-y6#MQQb~1TzOHdG1?LwPMCo z7;M%haC;m%b|pmm=oR<&iP2_E%4az{uDM=CZhw_1gGuF40jk`!cLU}_%Zgi}X%l6( zwrVAhtOw2`m{62%1lu& zQUzj!Sn!24C3a$$Q^xwY=32OMx)l|V8fnUn;g^34m}}bqva1_yJW0{6zk#0 z9Qo8{f7XrQYlrQ}rfqg+=KcuuAtYZ?IlIuL?xG1GEcGRvBGqJ*KWmjhE^_%zliA~2a;3xn z09ki(1*CQUN<_D(o&VYBxe3tovOb38HE4t$m~B@^VQq zJTKBYX`wV3gg3g^ZWAsV!^x}_cBXt;j%7M{Tp9D6UB=hgMNGZPBRI5Roh;MJc(?@h zz7(DEbopU=T(KelsSl$JX0-A*lK=@s82I>SR`F*SGj z5E8IXqELjz=N=&`WigYe`Zyy!rC&zHc~TFZg@(7Qe$&=?xt-k&GDP>LA$wRPr%&g% zm+&f9bb87c?Wi zAJQ9DFp9BO)@_#@ZuCpit`cp=i8m>&kRYI04=`n~+U;x9A1m-C}j zl%qDJ!XynpV`+mo!uFDRTTZl}KDRLpE=h0=je2 z=uUmbW_rHI59qh=`a+R>z`R(RYj|S0rw6aBoF>hpQT59s~eHDJtRW)3ela_ z&kOM{@vQXxXwlr+RS%0lgYSQV`aJ&x8K2POVeENTAfaIRQTnVzBU;_R(7S>U{l_!8 zKo8hgXUqT;@lBYC%mu`uTCHeFVt6hM(!S(JE=_}r(TM>+9&vb+pwtJZ7yLW(80m;V zYzPjMT0D{4T3fg?x0=<`gzmP5k~m{-(HfHZ+Dg5ef9mDcDMjwI%XV7#?}gF5a(E#$ zfKiVu1qu_%tSmswUw$BcaJpALcq3AqHRL(oHhGk z%Wd&CYTC8~kgGfzdp#muvxIHPur234YKt<8S3|(7YiH|z>gRs~ z6cSj8$CYH{E2IQCn^Ip}C5s}x=rXYf|l_kYI z+8JGOifG-!rd6SqFzl(_Q9uP!Z(H)M^7hEPI_c)|7({GO>bJ>7aF+N0Apr*FBP?jN z1V7J$Kj2-l_v$r=O)<|pE*95LXnAj&Em(l zyB1BS#R!^VJzIpUf^&Wh-nkW}p^-!n=gQ?ma`6txOtVc8u~X4BnpmhJmRR3bEwLf? zL6P$9$1EjcW@^qb)Hq1~l5$-B9(cUzByF!f<>(eLktoHedMD1ur%MV`noM{kTQ3HTOoTD+1Vi zPsL4$s^xIhyB#r7eXz2j<0&{~r!v45`{9E&h%a=iszN7`qd*F$kb>_aerK}1MG z7pvU|GW*QWD#bVTL|V{Y+zaA(#-5yGR6(x6-++j>zI~x>5daQaaApiOeA_5@vOcez zH~Y9SY~mv4cETfDDr3jjX61ckdba>{m6?3Pa|V32*FM*=h`q2AGO5<2x|nS79` zfhaed`3_Pp^f*Z1c1cO&+qpX`Dr35ku0ifz<*R+8jwuWa{!IV`lps1I5^RP?0%B$e zgG)WkOaBS1p^-=T%GZ(%pgUS)iPBkZmk{EA*J`90v{27|00E5q8lA_H|AC)nwcJv#}$^G-&kBx!E^!Fc(F2U~f zveiU~jz|PYt~Up+UupMt?RIXwp)<8Iv!8ZX4ss_6)(=e__TSMp>KWY4Uf#Xd7R#THxDktCjmiSZD~HMO>zgB0-(DP3suVTW>ERJ`>X`f! zW&f{8hU!q9%k1m1{~65a!T!OigWnp3uL6^i43z1%Lk6i1+J}y~+}?7?J^t&D{*5`^ z)w0Pj9@Qyi{ry-;8?YgEuG&}arX~-qE-AKhYq$(lsOpv8@>1mK;YRku9NJ~$@BUwI zc@hJUxw(7u=-su0ZoBA(O+uV2!QqC^{RAX?*6MgI>7FmO?mv48NB!7~NT?!tgEs*2 zT@8AD&-BCz;b|GW6yG=<_Zzqkpi(qaHQ2sqb$dwn0nWt2UKOwZ`+ z{V1ENmWCVZu+`5dWft*n{gdnev`^)=KRouN-U?QAHv#o~-#k@g&jI7ZLDs3r3avJD z%+IJD;#Sk7l*es=^@s$7*i70d1Pws<)e+Is zldBdE#KA5IypM7VgI!S2%6RYikkR+q&DRcBKiJD3QMhuPT36&OqxH>04lgcI!q3)S zKH;Er!Pa5s5M24%1?uB%`QkF77gF9HRbT6I%(c)F!c?a@KZeC;*yZ>4eVp>1__(9S zTKS6y?~c}4lS%l}wjwor%Ma;3p5}pBNH8UHrrY9V<(s*WTUu3Q<;~vqbf_^`9=0iy zT!K{w$PvucV0@}^uBs^m%d$I9Q6-4sw;tm3aHhxc>m3x#&3(mfW|*hD22M8=n>j(RN0Y0$ZTJj&c3vm!Uo10$AoCeF@qs5dpO2ih2j&k<`1 zA8D)+t(p6qi`L|1+ilzuQ9TaD9z74eT^dJcXCzVw^~>7=BjMjvk+XdB@24Y%h?O;9%Kl50<#e-P?MzIV?0Tgl$zY3CAs2qOj8ZKm&skzNQfxD&YB!?`q7 zXikHgS`2YZlQK{hSMVGH_zj_0haXZq*C&IsKdGJ}6>yK(E?AFBtpssaf;qQ(0;5OXj=o9<4F1=rAE;BIMtc@@e=eKF+~jO7e5;t7zk)Tkq1<9+Ajz6 z9}EEW`^rC`p+RWiKv`&fqya+Ddmjew5Ax45R8is7kA*9 zEZG4+$^%yr2ln65i62O4JGlRDzrfzR|L8}ca;PhRxBhqcz`MV@2YU2(_x=MPfy)1* z$QGqlKcEvPPGN$)CBMUX`U@a44aD%uZ+;3?vyc>Ly zQ5P&*uMfCUV}`9Cr;|^nzJC2fS_R>dOF{ffq!Rf6KG?q6USG|RILyVyRa2_A=Sy}kPJKHk)OlxDxL};qUcF(Xa<~u1?2S7ei*)(A{^}I&Ls#q6^XQQ5 z=s0WEj4H)rE4phv1aRcccy7!u7dc_qsvWyhXbT5Kb2cSakrP|9Gc7Zv&Yn_d?-fak z8hgO?OC9BEBo`L3YD(Lu4Kjz>9C?0R*?|#iU>mY zN6Zv%qip<7N?6}SWjU)+{qme`Wtwi@6nkf;=S0i|T^J~Nd|@Kw3f|bF-aoT*_G3ue zY0kJoE`R1_z!A5Ux;-)iyN45Y+se1-$fOv$i^H77QVnfh;la}eO9}EUHgg(Alz5e} z8wrj~9~8j%->A+duYN+gI_}BzBcF-eZ^&dtkSkJSWblq#*=6_6WOx9yZLO^A>_t%nKfo~QUJNw zQQ;pp!>2Q7(fDcMbI8!Pm`99mt2<*Rg^!a43XAG7Vy$WFC3eERMUC7Vh5LFj?$~fd z-H77j;gD-@1?Tj0lGUZNm$&GvITV_;&EHl|eAyM_U<@2-7s>>;%D|za2}}R;Y=Grh1~8j zg3G1QycvHp9wZoCjpK%YY3~({rZ8X0x7Oo$TZzamN3_s~b$m?QEy5$s2mEiXXD+i$ z&6lJ$x&QKb>q%J`^U-l7)^c!@fg#wX$B92WN^3Ie3jQDuzF$;(uBS(xpxR$xYEYtK zEVk}4XJV(G7VFqxydDD2T7s)fiF3Aic+0H8CwB-{!8C8Gix?)VJ{ZRciO%)n&DY8t z20~$C+#a3rQfmy%A;L2|eNz|(ka1q(Dpzk-jsICy^t)cC<5dZII#Sh`g(oumsY#q) zAKE8>GczmZX>)$NV@uqo{MN<``t-T5;=Z`{8Ww~rM%Hc{VH3SQOPIYex@1>5wFa}J z<_jEw77Ie)6aCY&y8>VcoCAS9+{2XDDQnz4-Y;sSkbXR3r=rmysqli zxSNTv7@;!Pge|vSRYQ!!bmPSiz1y8lp3Bu>>PJ&Zl5ZB4sP=7c_q%DDXFX(($z7?9 zGHx{P(OoE5Z^BZ+B7$1jsiaDJ#6+Uy6wHR7@Q1gc3d{o>Gh>yPMtB;2$Hd1PxDO}I z$+Z)~Zmp=y<}wJm6%vYmeirRA-p3U)z@^rIQB1A+*_oR}@#%EQ&ASXb>ZAp!(&>9X%oTTbXu@V@ zSBkGmVv%;pWo>P>{EKfU#k}wzJ-Um%1lR=2jY*czN(;KLi@G zofoc7%w=t}Sh{|wdNLb0A=~H4YY^EB4@OC^j&&CLC?+a|YL(k1&&l->n}*n>`nu_| z72D0m;0expO$`zzB?WGJA~o`f>&l6W=ju3rN{SBKTa7S^P}I(nm zEgSq6<|A8@7G85_MO4QSqrXrhX1FmP8QYgwF;C}C@Eputvo}j#sa$RC8G;eN6(N?$ zPDPbFm%BSsN>QTg*GKb=yYidS5-7*|W65j3Dx%KLE2-+InS(|>aAC={DyFD zGvME$ozYrrTcHvx9d@*+ug7InRF7R1!Ff$4 zd)kX}E|CF~J1f-@W~18fPA7FMc}DcjKLkScMs7YV2g(2Ghr{s#kzzai?b(aZGk_@vbn0AbhA)#TR zP^w<+XDsWzYLBKO{8T|i-wjOfv%31nTHU#`!j?tWm~&X}fKx7X3uc9JvxPllA35V% zny>+i6OVMdP;(AD*Se>^z?k{7@8LFAx(z0|{aDh@juyYEeLY@2Hz82~A5_>MY`bSx z0k5bEl8eu0w{!M;*BZJ8Ck8VdN3NDonXmJBgy0_;Xcn<3gtUKY3v8%#hBiqbfyPhz z3OR8$zvd^$biN|1$wse6!xCJhb#r~R$BN@^4R7UFK55||>08z>v6I-lMpbU3GlW4~ zjl|vApYawjkHL0Md_`MxWG1KIPIDo;8ObpDI|G~^-t9-D5Cn;C8%#z}U!29-g4rtz z1(wTaCO0y2OxmsPkp`X<&~s%%&l+b{!|KntG4rL>lkIj1$?jqT`Fi|swvPy$Vrbt<)qA^3oMA%$1!wxXy-tmNqOyr}&Yxo6o zv234vm1plHqE;GWntN`nv0=Y!>LsEKgHS=kM_xMw4xfAtFLMyjY-i#=zg38cq^~vlmPMbd_=#)alSZqk&>lC0kl^!R=_Vvd@9^pfE&pg5D*tHx4m)^J=YhJ z?C?#+`oMXkahCX6tSSTJV%Z|MH**F_JP+;I_^AU$y~4GsWkV?PA8sbu>j>D*To=(o z@$=6He0Oo^74>!=g0DyrBQnaOOJ+vKxMx7ZGaa7+pwqS4H2yiX@;htR!Ax>=^{re| zzQwtjP9T^|6N+>7_wr4HQ9)93adhsNa^43!y>$xKq}L$VYE4Ah9jS9{*t+$x?gnOK zwIJ!n{p)KA`^$zP2`Bt)7lx-dc`@3szM0n<-#;0yyT85VKlo>>L~Lr|<4*fY{|=C< z;`aYO%4F9A+tl-To!M?y_vy>ftqkMQFjvq179XkfQZD_R4a|fX%)s2Og5~LaYj3@m z+3Yk?{GQ=Q#>?HE&>e7|&!GJ*s;k8uea^$I+AZjfaoEo7EX_G_l(ErV?hpFQA~*#P zlc0&a$@v2N_`|)BrMldt&*L-aKs3_*ZPL_FB`cGqDQOt*6=Ike z9igqduz>yKlK-tyw0FbNfQDN@@x+TS`E!llmnUeP#BkUl^Q53fQ;M9ulx(at^~=Rm z=rgxOmzb6emtvL%^G|~BDyBZ!-Rmz9PSHAicSx%+!v=k=zJLpH;L74$DVNP~ewrki za%d0Fnr;JNVWT|YwALhQ%~Yi@B+Lj~sHt}Hqr9oPwKWsoon;kw4Ncq3)ST0H@a(d4 z>B4x9-hijMvB7ku3lHMJt2{BPYFoLLNVm9E#;vuO3R$v*bnAg3f_WTgw?g9Kt7CPh z)89OWJ=Al&7c%7*$eEv|=36SyguBJ*a{5tzeRsNMzSz-ehTc6((}ckN34G2qJJ{zA zeXa^Vzf?3jvWAqL1+70J&tA-Y8bmXb3VK4*-(3R2xGKPiUcJ<=x{$M0D+X29mSG;J zn0Pn=7*Ax$ZUUFxgPfmLMOjZZ?ccGg#`sEc)&64|L$hN`AhB#^B4xBtO zBU1ENzf}$NOM!U}v?f>E8k=o^t8|9O^V$w+*+PMR7>guo0};F|8Z}){;0FKf zuiMx?OF1h{mh8cgzCl@!DT9(VOc~IUqm^QmO)8PX-dfoLt+8Ny(cP>rX}?NR-I_gH z`oo=lI!n#K(4Q#zY{)5P_DARb)F~BM>G7G}UIM!Q^=vT$5*e`LJNPQdBeEO@RXD_8 zQnIi~IV!i9Z=L^^cqj32+cl3Fhpvqb8N8JZuu?{**Q_#nal~79xj@Ey0HQK zc&ct82L-Q#*TWm&3xmRWGg)m16OMuk|KCgs&%Bp+UmiUw8PB{6{&Dny19ueEtxTE8nQ3t$GsRrc$;!&osixc|H8pq1T@c1;CQDPkjiu$@ zSX#Ldx!?v?MpU?*(k2xzJArJRf@-t9X_*a)i=CVt7LzYUkkLT*`4%WwaQ}W@xwp;65TV=&neq6 z6gSTmt!r7VJ9h5O?Ohbs;k--iKm8w`o(hT#6C))-N3r&RR;62AKl1dGJE`@jMuMBAJxw`8+BYQb#aw z3!BU0^~TKVDfws_g5ZLiN3KCGr)+yV9iPyKBWt;sh?}8Q?M4dZdvWDpWV%B=u^>8x zh;LDn{$O@r?*15Ort-p{zByoVZootPJNxeCmTlzq?888cHPohmLx7G!`b{8910_+h zgAstvs71f|{kwKP8?)G@1VTzh00kPrG>0{8~TV;Jm_fUcFz%|o< zhBrH%ZL>3*a6Z{akVf3?e6!(prWYij z2iN65f0hd88hPa8%)}8rg!d7yv&2Z87cTIyxo@jaEUs!T(VD$`#J26tF7f`Qkt!~y zFhQc*f0zF6{Z32myl`MJ~D zDg9bxiF|dvAgO+@*pff&vdH~W6LE+uP)&z*OmSH$3nH%kOe74S!)l(5M(yY(A3!cp z%nGpRrOY^*$~`30g0J3JFIfZ^^w(F|2*>KhwVqwgW>XocdhrCR&Siwb;4_dzIK9s%oV2O_sI9u3yUN!tGyQl zV-yFq??h2({vYMRb3&BOjPg_5Ws=|6%tO;I#6)$J@Erye8UB`_zyqV;nVv_)JXVXa zU{^tzYp`Jf>=rau!jfbcD7v_h>>{z?Y4;+vc8)jTv`;@BWQ%GM6WY|f-A3UM(z#t! z-=@0CXwe|dt)|1I3=0Gobk0$Eo88EA56*1Eh;NK*lvY6%a%=O zx;wYP8?PqU7dC<7dP2GyHh}c5E{R^}FKT6?#D9>yMowkoRL81)-8Zot$MvJ)8Sif) z(owJ{r=PsqGk!FF@okc=M)O%$6^sWw$V^PQ@Cti%sGw>;6`LKmbiQG-dW`7l=*kQ^ zP$DO0zax@S(@T=wT1mF+gA=`mgT-3Hc0HRx_=PiT7utKK?)ip?=gjTlyB;E(euCoI znr*?LXPLxoB4IIS$<;kXJfJk+o2X8IOo7)E7TsM^+AX(otuVGS2D-twG)w|Hwpg`> z!5^zx1O{R=Ra7Ge&tvg+v-PjjY$keW)XbedmBPL@hpmwR~ zXV5Gtjq}&fAzO;!EEjVvn3;ycl!LmP^9=`vrsZ_7AXuI^qtK9XI{?_>zGEhoJ7*s{ zjNj=&!XSY@+x>u7VVcGbt&4~@+)eNX&iNub%i2()PWyWCJGlYh?iywW` zYafu@8zSNvkq@J6Y0e9(rSeZ~!PS2WeyF_cDBAC`zyceLcUI$7EjOt!A(*Lq^s8`W zwr&pM#r;&_n)kn7`LWYtj=_$gVDFNeqP11oxy)p=D|5^h)s3ZfIJl+GOIjo+V1gHx z^5H(s#_cRq1b%Jo4=4&DlYpAYBw_KleHC|>2gokG8Vk`!{ZKkT`Bt9OgGG}3W=%SX zO~#U^9j!>wmH?4SlclJ^a?VUTn!4p&AXfxm)ZF)1ycRPRqnS2CfB!<6Z2$wDBup^^ zLY@oFOMk8jxk3WhD~deQE=P;p5x>ux)iX^7u^NTXEyeFrPv7MW;uh{;{v{cYwp(ck}yn7%Ike^kIJG%>Co3i5|aY0|#Q5`XX?A*%s!+`YSWi-6Ea1k{%DP zqe9Y1`$jJxV47~>7qx!Uv>9l6VS1GmHdt+KaRxot;Xtzm6E0R`euc#h-6qzD)Nl%T zFIDWzjhYo9eg9DdyZ22WoOpU?jp^*xT63Y+)?q{6W?l1r?Ua1M^SW1tE#|5Eb<;1x z8x6=?Sh<(q?(q|g7H+$wwLljGX`wrg=SB}+=S0;nx^bh%qi>@jVdJi(MO^5>i>obu z=_H^&Aq&{%P^#S8sC+AM)T%*i)IJ+d>MnbI84DS3%KE9=DPs6Ij$DA4BH+(Z4AbaG z)ZGsJh}FF3O57!r?UWaWIXLNqjS6Rs#v*RaK9f~a@GtbZaLF}pu7mSd2RptfNwtSM zWibdtX)eN)%jx=V8Nu~S^MjK5rHPOvdYwePNV8V5DDObF=comzpQG$fu;llJaZ=1C zk3ksGUi~ph5tqDSJ_I$JiNbDUWkoS!I9XxMdrT?{pUJHaxnUW?sPmL7MAl_5_#X&f z5DwLCR2VqQx(#IO#j4q6vn1LSH>%7Eiw^{1G`n@NW~@{PO4ZN|>NGo~L^d%9uBA8G zLl@o;>MsCMt)2|qd2*-8&iCNz_kRvf0l|DMPnz+m^P?507IL(YHJi^iqRbTP1tf4= zJ_c%fsq@*`8w1e|!{*)1hg{(#)isb8)kwP=D!yu-Hs9}PV9j}UWgQG_bMno;4U`Av zp4AmF=!h8>krqOGQWBx#|25R??6^;$Dhu_Z^tU|db~HAso`YRSe;KZ^jvy3ZwMdOhYm4zQ{@nE z3E?|%NB14JdUg8UjctaB-`+Ue^gJo_#-F)%D^C7AaiTq7gWVYl|a_>?)7bfP?go-qh@w;Hjam%k@xgD+prI; zA22xqT^uh<()>-ndq48(3x8Gi*@BZCFLJBk8F%m@!Q4McjSc4vwAq67WcF1lKQU0M zxYu`y*s~4qE}gi0Ui#SKKAoQ_{vU@#ybB0WUJZ9MeeqzPYm%*L!@-!(9D9Bvv$bbk z_x@{x&XIjD&uRUXknqAELS;G6TSX!@!CE@@evl2ks?HrPZci^BXL_l_knnn|zyq*} z8!lj~OR@u)O7RwmW?T4PMlO|ru~plqw6O{vMWP*l%rU!@KP#WQHKeoHDUP0@y^3WT zwAvx-MT%L2_NBpczDM=BN&|~4 z{~r@QSA=gYVN*&#&GWsO?*ke_o=7!`cr<%;35KpnW>&!Z6Z5;|8}GFAg*u;)S3lha$L!n{|k>`!+T_0d+%#rWwV7)F6nhPb(C0R=$%@wdZDd2*TyG zt(|@cZFhY0s=rUxzMj4jRC>0i;mvmTJ7Z6BYd{%2Su-J?`CSOzS`(km9h9%@Mi@F% z(f|)v@_Ir(<<6}r5AEwO@lIZLKl9~0`f`u+PiX|-$T&Ykgg zK!Z&7wQJY@AGUR@<6)q}-ElK-b}1&H1p^5jn!P27OvaZCuzYbP=k`@m&w1m~r?J!ORea_WL@OM33H(`nU}9n9JO&?^vwf z&S@^$-(~ELmE~-sAq{ZdI~xza|BvTS9ivy2pvMs7IGYR$M5Iu_!qCj7>QXnFvhxl^ zjJ6Xk9&CjMbQXJJ_Flz+EQ!$#(_C&)fI#4dE13(-13Q=RCIHsX;S}aF{J#S`J^g;7 zvNw3iE~GKn(lS{CQYt_!3Fk604JH>WM8$5q61;D#--(&qK25J0xZsrhi;v%6izbV-qxY< zIJ8BJ$_vkbE2q0720pHAxQhzgDAD5np0fq_8#bC1SDjZz*gxoU=$?PB==_7A%I<ijx6GfQ6R}=f9va{@SRvyb!`IxDHxvvY?tQ81^ zR4#Y!UQ|rpSQm**Pe{RFl`Lkh=(r@{gC+rG2qjW7aL^)QIsx-~$=xGv~kT{O+3A!nWlD|HC#SY-XEAei~(J zX1UPe_+`@8udYdPlPZ?V(+O8xHU|r!ixolz|Ao+h@7!<$&+fsOHCZp+_sZ+SS9n3} z56hN=F|=H0qwF$is`ATX;=ta%Yz~O;sSzuLPXBYbRtWv~&VAL`*(a98RbDE1Gkdvt zDbJl)mJ|faa-nzSmPymuv@9lAs?oAJ@Q>w`$<}h{BioQZrDwY+KOMzAF1AalK z-Xv+t)vS5b*l0fg;oomEZc>c$19G_R-*Etz=Obk+mLnt^!v6uYRAYOovdx0)!p`3Q_hGQuxrg>NI~d~!x2PsXzk1L(Y(X`8H$`A85e@N@`-$CC zIL*plzhBawCQL1y$Gpy2+-gaPth^cQo~B(PequG%1-4i`A#BjlsGPZ5m~&PE?bVU# z_qLms2w+0_z;+o6HN4Q-;SU9W=T}xCa&13rO}Tkay8@KS-m4-`&faPyju8e8h1lW zo$G2LpU=R{Pt)w6hJ-Sdm+8{`vZEH%06GV^p64@04K2mwSL-5vpxXE^dBj6?%Kb$4fC(L&~qfB!Dlfj zYIha0-z(RXF}Senlxf8z=Y1?qm1VvpFysvVV7tNYq%T3K=y<&e4g*3fy~mf0i@h_! z4-D-N);dPkXP}M?hap5ab1jdpl<*5xTG7vbuN=X4BiaIcdYoR#63CNGy|gz5mX@VV z*6c|C&B>aj9HN!wBwOVwFJ}j$ox9Y9vySZqw{R9EbbQ?|+)4370CN;?hp#QRvl!rc zb{+IRdE8ROs0yF#9;r}0WZ-5%z};Z&uE~yg*!nc2T)*5IDTNu}9u zgcc7~l|Vt;lj+UI`@32)B8T&}@?GUTv#a0#Mxk-ZIql&wN?9X5W8Sr6>Kk~cH}HBC zi{_{NMzV4l?tDA$J<&6QQqTgzI%7H?6k>#O^-3P;kEDpgnNySCoT&Del*tYEOit7W zi2FH{3;crQ5Q0OfR~KFqgz?O`9Hi#*alu((VX^a%M#k)10)Kv9GlHJ9u&J2aTN*GV~UYjvXqR!*!G&uIebY z#m%Zx-o0+X_#X@|Yf127Rg@(w%$F5(AP;!n8ne=E@y&7l(9s>`)XL^fa(%7 za3|FhL+JNbPd66}68MWL=3aedb?E8e&muRG0(KzZ*+X;bdN#`5c@-J;K2yib{4^T( zmyUFKTvM8!D?;T?3MQt6nx`U!IXe!ZJaNUI+r7F1uyyVy9S&j)&^XrqP=eoFjq-Y~ zhudNeZe!(oUez0Ln#)A1M=>7KOnfxpH{(MgcGpn-;{uKJ;O}>gU)g9T^XCtnx<3*!xI3Q|p zt#kdc^nel9IJel~)(wh<)sZz-)n>| zrF4q_sMCm%0`Dl+B_xMMxHKY9=(}FcdqJU%7nSOzd5ph4u%35*>_}O}z3c#X6F`CH z+pEuNqAlm&rIJ#dYgxvohX5{R*`l;AX*8up9Skbzq@0&-+ftJu=t6N=lIlUbpE_$I zt(C~iXU0o9S!=cJu53h_&qsUN$L@>QqT8~!Ob1JQP=XKmSyUN|z;X1bZc z#W&37h+0abwhX|>PR%COj(SDA*58QfVWI8QD^=J-W1X2J0EDA&@DiaKuS^T$u1Qy= zS7ok=Et>la$bi7OKwszP%>CMg8PQYE5&wqFsd+b0Fn|W-DwVtns7O{ADl6zl9`FLz z$A?lIDcvh2onSS#n=;#Ghmc81tl{S{1Ky_WOBp$0l8zzK1MHv!=_JKq9Nm?2O=%z+ zmGw9&d6!rpJ7K(m*MOcW8?0U&Usy_!C3J)<{y>#1UdsoF-0iNl6#c8Y(S3X3B$ybN z*&YqjO#oh+54^(I!_6q7%pQ8`Vt$ygg(=FRkXyHp9S zG}Oa2AH76U4w`87GGlcv{%^U@Fs1s(q|-Y630v4Q7D)i(eG*63)bMi+NmLp&sW6^> z>*S)T`O;lj0_k_Zp@L|Jml#7|dpQ<&!JQAhFf&ooi!O&B*my07 z8L8F3@7CsG!Ozz;T(53sPn>kR{68-1;kaD3SWn?u3`tzWE-}6RTKL37qc}3X`ufhxO+^9p%Hgyj0{9mfI;p~^W zYM?*417j+<+NhT$)NA%b!4MHSK;~w zGrxf=LiBx&J~-<6u|s8ZxNN^Y*gfRafpx|F^UuRh31egQ7Nw!4Y}V?+4}sh4VYwo=rh)7TM#-tO|llfrq%E2o5eT!RGN@TY-1B^in{L#)s zQYYXh|9YqS4pTn0sz3t<4yfE{wJ|B6P8Qe&^IIIWj|Lu|gWexKIRDZs1TTs` zQy6UQ#_b=jg7!A^6wEu!?|7@TUOg@1N8GP6gd7TdI7=V5@H-IHlm_qX?;AB@T`ErH zKkP>A-<%TjID5q8anVwut07q~wk#iebnNGRZ)Tv|r3z?eMw;Dlek#{FNZWd$c$@yw zvLOrCF>kY8g$?1QPqeqN0${i(SdQBJoA9jlL{w)oY1>u7y_g z?(Eb*@)DEisHHt7C`F4l9UnI*Jz~kv4w%ZmZ2Z43(q~_<5vIVzk3UrWS5tH+O{jbj zgz@5{kB^qMjat+WHow32*|IGWFMRy{`$Lw`$72rO6C(KsNmZPfYJ7wAMfU{FqU>xb+dK=&~~M*{n6Q^0f4Oa zFhByFB?my3_eileXj9ff6%dG7c@<;%W3O|{_B1P4`K8mbk|jP3pzsJChui_6vj^zC ztuPnW!|xI;%+jjhti*ac76z+34vOQQW==<2rvQ*B5{c~H4i(=C46x8(KN!_}A6ql? zR)=lsk4?bX%Sl>x-XLz zaGQV^*2)S1!fkj9#PAaico~4!WXoe_hY$F*-4B1er;u0c@m=}lp|=O(ggg{R&+*B( z6BTs#<%C_br;^{>TYET98aC>bo_onS%W)KNJK_b~Ut~uyoXpX*f>WG_`6(v=u>95E z=pu$w`6D%;+`r>-d6RK(``n8^HYAOh&$XZR1E{;>0BRW46I?rySi8BGpezHbcMznh zLQ#vNE4#$HZbq~7;Vp6C2M;LFlRED9ehUHGW6{aCi_SU|j2}RlJx^)Vs8;|AVjS4@ zGjaj+F@TOFr+K{OKWzc|n-~+qircyNv|n{rbtnPw(_hc`PR8{<;m%Hvr+tf6A2&$W zo201#d{KL5qBb)kBs}ClN<58_l)ly)04e!PcaBTmQ{tRzeKwee}GJY~4CQeV*PR%RAh{%|XeIADl0v9LnV4}KX@3ku! z<`~c&NMbKSOu5{OyOWOcwN7928O+LmF6qP*Q{7{St^jYl^({eN3mJdYd1P|SRmm%% z{a%&f2h7;o4cN~-j^!Cqm%1Z2_OWXo*<6u4VJO7|4F_D#3kc23@<*{A?xw?fMW5*? z^h1$}kz0oqD1|mDy)qS`1VuXp=LCwkW{q&G+-Ewd;ki27 zYscsLjdUhV7Q)PKeY(1znxHSm!HymSd{YI0h#hiINRhTRABbWpkBJF!8ejnu6+g3< zMQsU;vd^rS_}DDWjadEEgE%;+{{_gj$&ifBVD@UTbsVCAci290nl_2`K5h~Acpy6J zUuN`&q_ej}k#`4QEaPUg?*la^x;Fy}dK{crFF_rRFV)`qf%!!35><(`AeeL9t5s@A ze<8kHJ@C$fTfP{Y6$}FKyN~P~x=P@ZlaYoMl%KH~F|c>~f=o5ML34*7Ix6fCiHBgn zBS4d)Fw=TEqQ}#$z@^cO4#DG|P*=05x6(NK+vEn?`3*frHXu7jZyBRa>nMkTl{%f4 z2)d03@F~_&5n`m#4)XBp?rb~-AV6BSxKP{+T zZ-y0CTl4wf6m3{<=W5|9&PQT0mnHSb`yZJ;RH^jRo1dt&>R{<1l6~S+xPVcBzyBGB s{1}h=6TQwq$bO>jq`Im(64?xO`EHWjts&s0=~c&%*c~oCbm6!E1)`TNGXMYp literal 0 HcmV?d00001 diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.pdf.PreviewPdfFragmentScreenshotIT_showPdf.png b/app/screenshots/generic/debug/com.owncloud.android.ui.preview.pdf.PreviewPdfFragmentScreenshotIT_showPdf.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.preview.pdf.PreviewPdfFragmentScreenshotIT_showPdf.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.preview.pdf.PreviewPdfFragmentScreenshotIT_showPdf.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_differentUser.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_differentUser.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_differentUser.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_differentUser.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_empty_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_error_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_files_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_blue.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_blue.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_blue.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_blue.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_dark_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_black.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_black.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_black.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_black.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_white.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_white.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_white.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_loading_light_white.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_normalUser.png b/app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_normalUser.png similarity index 100% rename from app/screenshots/gplay/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_normalUser.png rename to app/screenshots/generic/debug/com.owncloud.android.ui.trashbin.TrashbinActivityIT_normalUser.png diff --git a/app/screenshots/gplay/debug/richworkspaces_dark.png b/app/screenshots/generic/debug/richworkspaces_dark.png similarity index 100% rename from app/screenshots/gplay/debug/richworkspaces_dark.png rename to app/screenshots/generic/debug/richworkspaces_dark.png diff --git a/app/screenshots/gplay/debug/richworkspaces_light.png b/app/screenshots/generic/debug/richworkspaces_light.png similarity index 100% rename from app/screenshots/gplay/debug/richworkspaces_light.png rename to app/screenshots/generic/debug/richworkspaces_light.png diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open.png b/app/screenshots/gplay/debug/com.nextcloud.client.CommunityActivityIT_open.png deleted file mode 100644 index 912c0b59e5030e9cdd08807939fd52bebefe6fc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76634 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